Normalizacja w analizie DOM z Javą - jak to działa?

240

W tym samouczku widziałem poniższy wiersz kodu analizatora składni DOM .

doc.getDocumentElement().normalize();

Dlaczego przeprowadzamy tę normalizację?
Czytałem dokumenty, ale nie mogłem zrozumieć słowa.

Umieszcza wszystkie węzły tekstowe na pełnej głębokości poddrzewa pod tym węzłem

Okej, więc czy ktoś może mi pokazać (najlepiej ze zdjęciem), jak wygląda to drzewo?

Czy ktoś może mi wyjaśnić, dlaczego potrzebna jest normalizacja?
Co się stanie, jeśli się nie znormalizujemy?

Młynek do jabłek
źródło
Niezależnie od pytania, przeczytaj notatkę na przykładzie: „DOM Parser działa wolno i zużywa dużo pamięci, gdy ładuje dokument XML zawierający dużo danych. Rozważ parser SAX jako rozwiązanie, SAX jest szybszy niż DOM i zużywają mniej pamięci ”. .
wulfgarpro
3
@ wulfgar.pro - Rozumiem, co powiedziałeś. Ale chcę zrozumieć rzeczy, które zadałem w pytaniu. Wkrótce zrobię też analizę SAX.
Apple Grinder,
Wyszukiwanie w Google hasła „normalizuj xml” dało wyniki, które wydają się przydatne. Wygląda to podobnie do normalizacji w bazach danych.
Apple Grinder,
2
@EJP - umm ... wciąż nie jest jasne, ponieważ nie znam dogłębnie xml i czytam tylko kilka stron wprowadzających. BTW, nie zrozum mnie źle, zrobiłeś dokładnie to, co zrobił autor dokumentu - używając skomplikowanych słów zamiast zwykłego angielskiego (zwykły jak szczupak = łatwa do zrozumienia). Najpierw proste słowa, a później żargon.
Apple Grinder,
7
W chwili pisania tego tekstu wspomniana witryna internetowa odwołuje się do tego postu SO. Mój mózg właśnie wyrzucił błąd zależności.
chessofnerd

Odpowiedzi:

366

Reszta zdania to:

gdzie tylko struktura (np. elementy, komentarze, instrukcje przetwarzania, sekcje CDATA i odniesienia do encji) oddziela węzły tekstowe, tj. nie ma ani sąsiadujących węzłów tekstowych, ani pustych węzłów tekstowych.

Zasadniczo oznacza to, że następujący element XML

<foo>hello 
wor
ld</foo>

może być reprezentowany w ten sposób w zdenormalizowanym węźle:

Element foo
    Text node: ""
    Text node: "Hello "
    Text node: "wor"
    Text node: "ld"

Po znormalizowaniu węzeł będzie wyglądał tak

Element foo
    Text node: "Hello world"

To samo dotyczy atrybutów: <foo bar="Hello world"/>komentarzy itp.

JB Nizet
źródło
2
Aha ! teraz jest o wiele wyraźniej. Nie wiem o strukturach danych (???) i węzłach. Ale rzuciłem okiem na strukturę drzewa i domyślam się, że komputer może przechowywać „witaj świecie” w sposób, który zasugerowałeś. Czy to prawda?
Apple Grinder,
9
Musisz nauczyć się podstaw na temat DOM. Tak, DOM reprezentuje dokument XML jako drzewo. A w drzewie masz węzeł główny mający węzeł potomny, każdy węzeł potomny również ma węzły potomne itp. Takie jest drzewo. Element jest rodzajem węzła, a TextNode to inny rodzaj węzła.
JB Nizet,
7
Dzięki JB Nizet. Nie mogę powiedzieć, jak bardzo ulżyło mi po uzyskaniu wskazówek.
Apple Grinder,
2
@ user2043553, nowe linie są w rzeczywistości o to chodzi. Bez nowych linii nie zobaczyłbyś różnicy. Jeśli nie powinieneś był rozumieć: Normalizacja „poprawia” XML, więc jeden znacznik jest interpretowany jako jeden element. Jeśli tego nie zrobiłeś, może się zdarzyć, że te nowe znaki są interpretowane jako separatory między kilkoma elementami tego samego typu (odpowiednio w tym samym znaczniku).
Stacky
1
@Stacky, w przykładzie są dwie nowe linie, nie są wyświetlane po normalizacji w przykładzie, co może sprawić, że ludzie uwierzą, że już ich nie ma. Wynikowy węzeł tekstowy z wyświetlonymi znakami nowej linii wyglądałby następująco: „Cześć \ nwor \ nld” Normalizacja nie usuwa znaków nowego wiersza.
Christian
10

Mówiąc prościej, normalizacja to redukcja zwolnień.
Przykłady redundancji:
a) białe spacje poza znacznikami root / document ( ... <document> </document> ... )
b) białe spacje w tagach początkowych (< ... >) i końcowych (</ ... >)
c) białe spacje między atrybutami i ich wartościami (tj. spacje między nazwą klucza a = " )
d) zbędne deklaracje przestrzeni nazw
e) podziały linii / białe spacje w tekstach atrybutów i znaczników
f) komentarze itp ...

AVA
źródło
7

Jako rozszerzenie odpowiedzi @ JBNizet dla bardziej technicznych użytkowników, oto jak wygląda implementacja org.w3c.dom.Nodeinterfejsu com.sun.org.apache.xerces.internal.dom.ParentNode, daje wyobrażenie o tym, jak to naprawdę działa.

public void normalize() {
    // No need to normalize if already normalized.
    if (isNormalized()) {
        return;
    }
    if (needsSyncChildren()) {
        synchronizeChildren();
    }
    ChildNode kid;
    for (kid = firstChild; kid != null; kid = kid.nextSibling) {
         kid.normalize();
    }
    isNormalized(true);
}

Przebiega rekursywnie przez wszystkie węzły i wywołuje kid.normalize()
Ten mechanizm jest nadpisanyorg.apache.xerces.dom.ElementImpl

public void normalize() {
     // No need to normalize if already normalized.
     if (isNormalized()) {
         return;
     }
     if (needsSyncChildren()) {
         synchronizeChildren();
     }
     ChildNode kid, next;
     for (kid = firstChild; kid != null; kid = next) {
         next = kid.nextSibling;

         // If kid is a text node, we need to check for one of two
         // conditions:
         //   1) There is an adjacent text node
         //   2) There is no adjacent text node, but kid is
         //      an empty text node.
         if ( kid.getNodeType() == Node.TEXT_NODE )
         {
             // If an adjacent text node, merge it with kid
             if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
             {
                 ((Text)kid).appendData(next.getNodeValue());
                 removeChild( next );
                 next = kid; // Don't advance; there might be another.
             }
             else
             {
                 // If kid is empty, remove it
                 if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) {
                     removeChild( kid );
                 }
             }
         }

         // Otherwise it might be an Element, which is handled recursively
         else if (kid.getNodeType() == Node.ELEMENT_NODE) {
             kid.normalize();
         }
     }

     // We must also normalize all of the attributes
     if ( attributes!=null )
     {
         for( int i=0; i<attributes.getLength(); ++i )
         {
             Node attr = attributes.item(i);
             attr.normalize();
         }
     }

    // changed() will have occurred when the removeChild() was done,
    // so does not have to be reissued.

     isNormalized(true);
 } 

Mam nadzieję, że to pozwoli Ci zaoszczędzić trochę czasu.

Matas Vaitkevicius
źródło