HTML ist eine sogenannte Auszeichnungssprache, das ist, der Text wird (syntaktisch) so strukturiert, dass sie in erster Linie maschinenlesbar wird, aus der dann' ein Parser ein sogenanntes Document Object Model (DOM) generiert. Das DOM ist dabei hierarchisch aufgebaut (Wurzel, Eltern/Kind-Knoten) und erleichtert weiterführende Operationen wie das Rendern der Webseite, oder der Manipulation (etwa mit Javascript) von Inhalten eben dieser Webseite.
Im Folgenden soll ein simpler DOM Generator präsentiert werden.
Der Prozess besteht aus zwei Phasen:
In der ersten Phase wird der Text tokenisiert (die Zeichen also in logische Einheiten zusammengefasst, im gewissen Sinne kontextualisiert):
Spoiler anzeigen
Im zweiten Schritt wird' aus den Tokens dann das DOM generiert:
Spoiler anzeigen
Möglicher Aufruf:
Viel Spaß.
Im Folgenden soll ein simpler DOM Generator präsentiert werden.
Der Prozess besteht aus zwei Phasen:
In der ersten Phase wird der Text tokenisiert (die Zeichen also in logische Einheiten zusammengefasst, im gewissen Sinne kontextualisiert):
C#-Quellcode
- enum TokenType
- {
- BracketOpen,
- BracketClose,
- Slash,
- String
- }
- struct Token
- {
- public TokenType TokenType { get; set; }
- public string? Data { get; set; }
- }
- IEnumerable<Token> Tokenize(string html)
- {
- Dictionary<char, TokenType> TOKENS = new Dictionary<char, TokenType>()
- {
- { '<', TokenType.BracketOpen },
- { '>', TokenType.BracketClose },
- { '/', TokenType.Slash },
- };
- StringBuilder charAccum = new StringBuilder();
- bool bracketOpened = false;
- int whiteSpaces = 0;
- for (int i = 0; i < html.Length; i++)
- {
- var c = html[i];
- if (TOKENS.ContainsKey(c))
- {
- if (charAccum.Length > 0)
- {
- yield return new Token()
- {
- Data = charAccum.ToString(),
- TokenType = TokenType.String
- };
- charAccum.Clear();
- }
- yield return new Token()
- {
- Data = c + "",
- TokenType = TOKENS[c]
- };
- if (c == '<')
- bracketOpened = true;
- else if (c == '>')
- bracketOpened = false;
- }
- else
- {
- if (bracketOpened && c == ' ')
- {
- if (whiteSpaces > 0)
- {
- continue;
- }
- else whiteSpaces++;
- }else if (bracketOpened && c!=' ')
- {
- whiteSpaces = 0;
- }
- charAccum.Append(c);
- }
- }
- }
Im zweiten Schritt wird' aus den Tokens dann das DOM generiert:
C#-Quellcode
- class HtmlNode
- {
- private StringBuilder _attributeBuilder = new StringBuilder();
- private StringBuilder _contentBuilder = new StringBuilder();
- public List<HtmlNode> Children { get; private set; } = new List<HtmlNode>();
- public string? Type { get; private set; }
- public string Attribute => _attributeBuilder.ToString();
- public string Content => _contentBuilder.ToString();
- public void PutAttribute(string? data)
- {
- string[]? args = data?.Split(' ');
- if (args == null) return;
- Type = args[0];
- _attributeBuilder.Append(string.Join(' ', args.Skip(1)));
- }
- public void PutContent(string? data)
- {
- _contentBuilder.Append(data);
- }
- public override string ToString()
- {
- return $"[{Type}] {Content}";
- }
- }
- HtmlNode Html2DOM(IEnumerable<Token> tokens)
- {
- HtmlNode root = new HtmlNode();
- Stack<HtmlNode> nodeStack = new Stack<HtmlNode>();
- var enumerator = tokens.GetEnumerator();
- while (enumerator.MoveNext())
- {
- var token = enumerator.Current;
- if (token.TokenType == TokenType.BracketOpen)
- {
- enumerator.MoveNext();
- HtmlNode currentNode = new();
- if(enumerator.Current.TokenType == TokenType.Slash)
- {
- nodeStack.Push(root);
- enumerator.MoveNext();
- var endingType = enumerator.Current.Data;
- var endingTag = nodeStack.Pop();
- List<HtmlNode> nodeAccumulator = new List<HtmlNode>();
- while (endingTag.Type != null && !endingTag.Type.Equals(endingType))
- {
- nodeAccumulator.Add(endingTag);
- endingTag = nodeStack.Pop();
- }
- foreach (var child in nodeAccumulator)
- {
- endingTag.Children.Add(child);
- }
- root = nodeStack.Pop();
- root.Children.Add(endingTag);
- }
- else
- {
- nodeStack.Push(root);
- currentNode.PutAttribute(enumerator.Current.Data);
- root = currentNode;
- }
- continue;
- }
- if (token.TokenType == TokenType.BracketClose) continue;
- root.PutContent(token.Data);
- }
- return root;
- }
Möglicher Aufruf:
Viel Spaß.
Und Gott alleine weiß alles am allerbesten und besser.