2012-12-20 12 views
5

Mam nadzieję, że ktoś po prostu wskaże coś oczywistego, czego mi tu brakuje. Czuję, że zrobiłem to setki razy i z jakiegoś powodu dzisiejszego wieczoru zachowanie z tego powodu rzuca mnie na pętlę.Groovy Node.depthFirst() zwraca listę węzłów i ciągów znaków?

Czytam w jakimś XML z publicznego API. Chcę wyodrębnić cały tekst z określonego węzła (wszystko w obrębie "treści"), który obejmuje również wiele węzłów potomnych. Prosty przykład:

<xml> 
    <metadata> 
     <article> 
      <body> 
       <sec> 
        <title>A Title</title> 
        <p> 
         This contains 
         <italic>italics</italic> 
         and 
         <xref ref-type="bibr">xref's</xref> 
         . 
        </p> 
       </sec> 
       <sec> 
        <title>Second Title</title> 
       </sec> 
      </body> 
     </article> 
    </metadata> 
</xml> 

Więc ostatecznie chcę przechodzić przez drzewo w pożądanym węzła (znowu, „ciała”) i wyodrębnić cały tekst zawarty w swojej naturalnej kolejności. dość proste, więc po prostu napisać ten mały Groovy skrypt ...

def xmlParser = new XmlParser() 
def xml = xmlParser.parseText(rawXml) 
xml.metadata.article.body[0].depthFirst().each { node -> 
    if(node.children().size() == 1) { 
     println node.text() 
    } 
} 

... po czym udają się wysadzić z „No podpisania metoda: java.lang.String.children()”. Więc myślę sobie "poczekaj, co? Czy zwariowałem?" Node.depthFirst() powinien zwrócić tylko listę węzłów. Dodaję małą próbkę "instanceof" i na pewno otrzymuję kombinację obiektów Node i obiektów String. W szczególności linie spoza jednostek znajdujących się w tej samej linii są zwracane jako ciągi, zwane także "To zawiera" i "i". Cała reszta to węzeł (zgodnie z oczekiwaniami).

Potrafię to obejść łatwo. To jednak nie wydaje się być właściwym zachowaniem i mam nadzieję, że ktoś może wskazać mi właściwy kierunek.

+0

Jak daleko jak mogę powiedzieć, Node.depthFirst zachowywał się tak, jak można by oczekiwać w groovy 1.7. W wersji 2.0+ widzę takie same wyniki węzłów/ciągów. – Joseph

Odpowiedz

7

Jestem prawie pewien, że to prawidłowe zachowanie (chociaż zawsze uważałem, że XmlSlurper i XmlParser mają słabe API). Wszystkie rzeczy, które można iterować, powinny zaimplementować IMO interfejsu węzła i potencjalnie mieć type z , którego można użyć, aby uzyskać tekst z nich.

Te węzły tekstowe są prawidłowymi węzłami, które w wielu przypadkach trzeba by uderzyć, ponieważ wykonały pierwszy ruch w głąb XML. Jeśli nie zostaną zwrócone, Twój algorytm sprawdzania, czy rozmiar dzieci równy 1 nie zadziała, ponieważ niektóre węzły (takie jak znacznik <p>) zawierają zarówno mieszany tekst, jak i elementy.

Ponadto, dlaczego depthFirst nie zwraca konsekwentnie wszystkich węzłów tekstowych, w których tekst jest jedynym dzieckiem, na przykład dla italic powyżej, pogarsza jeszcze sytuację.

staram się jak korzystać z podpisu metod porywające pozwolić postać wykonania na zewnątrz, który jest właściwy sposób obsłużyć każdy węzeł (zamiast używać coś takiego instanceof) tak:

def rawXml = """<xml> 
    <metadata> 
     <article> 
      <body> 
       <sec> 
        <title>A Title</title> 
        <p> 
         This contains 
         <italic>italics</italic> 
         and 
         <xref ref-type="bibr">xref's</xref> 
         . 
        </p> 
       </sec> 
       <sec> 
        <title>Second Title</title> 
       </sec> 
      </body> 
     </article> 
    </metadata> 
</xml>""" 

def processNode(String nodeText) { 
    return nodeText 
} 

def processNode(Object node) { 
    if(node.children().size() == 1) { 
     return node.text() 
    } 
} 

def xmlParser = new XmlParser() 
def xml = xmlParser.parseText(rawXml) 
def xmlText = xml.metadata.article.body[0].'**'.findResults { node -> 
    processNode(node) 
} 

println xmlText.join(" ") 

Drukuje

A Title This contains italics and xref's . Second Title 

Alternatywnie, klasa XmlSlurper prawdopodobnie nie więcej, co chcesz/oczekiwać, że i ma bardziej rozsądny zestaw wyjściu z metody text(). Jeśli naprawdę nie trzeba robić jakichkolwiek DOM spaceru z wynikami (co XmlParser jest „lepiej” dla), sugeruję XmlSlurper:

def xmlParser = new XmlSlurper() 
def xml = xmlParser.parseText(rawXml) 
def bodyText = xml.metadata.article.body[0].text() 
println bodyText 

Wydruki:

A Title 
        This contains 
        italics 
        and 
        xref's 
        . 
       Second Title 
+0

Ostatnia ciekawostka to wszystko, czego naprawdę chcę i dokładnie to, czego się spodziewałem.Mógłbym przysiąc, że próbowałem tego, ale musiałem właśnie zostawić tam DeepFirst() za każdym razem, kiedy rzuciło mnie na pętlę. – James

+0

Aha, i to, że GPathResult.text() zachowuje się zupełnie inaczej niż Node.text(). Szkoda, że ​​doktorzy nie byli bardziej informatywni ... – James

+0

Czy możesz podać znaczenie podwójnego "*" –

Powiązane problemy