2016-07-06 16 views
3

Ich habe ein Objekt, das derzeit von einem Java-POJO zu XML mit JAXB zugeordnet ist. Sobald ich diese XML habe, muss ich sie gelegentlich auf eine Auswahl von Elementen reduzieren, die auf der Eingabe durch einen Benutzer basieren. Das Ergebnis sollte XML mit NUR den angegebenen "Feldern" sein.Wie filtere ich XML-String für Element "Pfade" in Groovy oder Java

Ich bin auf eine Reihe von ähnlichen Anwendungsfällen gestoßen, die uns SAX Filter betreffen, aber sie scheinen sehr kompliziert zu sein und die Antworten bringen mich nicht ganz dorthin, wo ich sie brauche. Das nächste Beispiel ist this one, das einen einzelnen Pfad vom Ergebnis ausschließt. Ich möchte das Gegenteil - Whitelist eine ausgewählte Liste von Elementen.

Beispiel Objekt: School.xml

<SchoolInfo RefId="34060F68BE3942F1B1264E6D2CC3C353"> 
     <LocalId>57</LocalId> 
     <SchoolName>Foobar School of Technology</SchoolName> 
     <Principal> 
      <FirstName>Bob</FirstName> 
      <LastName>Smith</LastName> 
     </Principal> 
     <StateProvinceId>34573</StateProvinceId> 
     <LEAInfoRefId>340666687E3942F1B1264E1223453C353</LEAInfoRefId> 
     <PhoneNumberList> 
      <PhoneNumber Type="0096"> 
       <Number>555-832-5555</Number> 
      </PhoneNumber> 
      <PhoneNumber Type="0096"> 
       <Number>555-999-5555</Number> 
      </PhoneNumber> 
     </PhoneNumberList> 
    </SchoolInfo> 

In Anbetracht der folgenden Eingabe als "Filter":

List<String> filter = [ 
    "LocalId", 
    "SchoolName", 
    "Principal/FirstName", 
    "PhoneNumberList/PhoneNumber/Number", 
] 

muss ich die Ausgabe sein:

<SchoolInfo RefId="34060F68BE3942F1B1264E6D2CC3C353"> 
    <LocalId>57</LocalId> 
    <SchoolName>Foobar School of Technology</SchoolName> 
    <Principal> 
     <FirstName>Bob</FirstName> 
    </Principal> 
    <PhoneNumberList> 
     <PhoneNumber Type="0096"> 
      <Number>555-832-5555</Number> 
     </PhoneNumber> 
     <PhoneNumber Type="0096"> 
      <Number>555-999-5555</Number> 
     </PhoneNumber> 
    </PhoneNumberList> 
</SchoolInfo> 

Was ist Die beste Bibliothek, um dies zu erreichen? SAX Filtering fühlt sich zu kompliziert an und XSLT scheint angesichts der dynamischen Filterung nicht gut zu passen.

Beispiele, die mir näher kommen, wären sehr willkommen.

+0

'Groovy' sollte gut sein mit' XmlParser' oder 'MarkupBuild'. Sehen Sie ein Beispiel [hier] (http://mrhaki.blogspot.in/2011/05/groovy-goodness-change-xml-structure.html) – Rao

+1

Suchen Sie nach Codebeispielen, die dies bewerkstelligen oder nur nach Empfehlungen zu libs suchen? –

+0

@ vtd-xml-author Ein Code-Beispiel wäre toll, aber ich bitte niemanden, die Arbeit für mich zu erledigen. Ich suche Ratschläge für die richtige Bibliothek und die Methode in dieser Bibliothek. –

Antwort

0

Dies ist der Code, der die White List ausführt ... Er basiert auf XPath und VTD-XML. Sein Ausgang hat Probleme Einbuchtung ... dies der erste Durchlauf ist, die Richtigkeit ...

import com.ximpleware.*; 
import java.io.*; 
import java.util.*; 

public class whiteList { 

    public static void main(String[] s) throws VTDException, IOException{ 
     VTDGen vg = new VTDGen(); 
     List <String> filter = Arrays.asList("LocalId", 
       "SchoolName", 
       "Principal/FirstName", 
       "PhoneNumberList/PhoneNumber/Number"); 
     if (!vg.parseFile("d:\\xml\\schoolInfo.xml", false)){ 
      return; 
     } 
     VTDNav vn = vg.getNav(); 
     FastIntBuffer fib = new FastIntBuffer(); 
     // build a bitmap for the entire token pool consisting of elements 
     int i,k; 
     for (i=0;i<vn.getTokenCount();i++){ 
      if (vn.getTokenType(i)==VTDNav.TOKEN_STARTING_TAG){ 
       fib.append(0x1);// b'11 since it is a white list, 
      }else{ 
       fib.append(0); 
      } 
     } 
     AutoPilot ap = new AutoPilot(vn); 
     AutoPilot ap1= new AutoPilot(vn); 
     ap1.selectXPath("descendant::*");// mark descendant as keep 
     for (int j=0;j<filter.size();j++){ 
      ap.selectXPath(filter.get(j)); 
      while((i=ap.evalXPath())!=-1){ 
       fib.modifyEntry(i, 0x3); 
       vn.push(); 
       do{ 
        if(vn.getTokenDepth(vn.getCurrentIndex())>=0) 
         fib.modifyEntry(vn.getCurrentIndex(), 0x3); 
        else 
         break; 
       }while(vn.toElement(VTDNav.P)); 
       vn.pop(); 
       vn.push(); 
       while((k=ap1.evalXPath())!=-1){ 
        fib.modifyEntry(k, 0x3); 
       } 
       ap1.resetXPath(); 
       vn.pop(); 
      } 
      ap.resetXPath(); 
     } 

     //remove those not on the whitelist 
     XMLModifier xm = new XMLModifier(vn); 
     for (int j=0;j<fib.size();j++){ 
      if (fib.intAt(j)==0x1){ 
       vn.recoverNode(j); 
       xm.remove(); 
      } 
     } 
     xm.output("d:\\xml\\newSchoolInfo.xml");      
    } 
} 
+0

Ich teste das jetzt. Eine sofortige Änderung ist, dass ich aus einem String lesen und als String ausgeben muss. Ich gehe davon aus, dass ich ByteArrayInputStream und sein Äquivalent verwenden kann? –

+0

Dies führt tatsächlich zu einer "NullPointerException" für mich, wenn ich xm.output auf einem 'ByteArrayOutputStream' aufruft. Es fällt mir schwer, der Ausführung zu folgen, aber es sieht so aus, als ob es keine VTD-Token gibt, wenn es versucht, die Ausgabe zu schreiben? –

+0

Können Sie den Code, den Sie bisher haben, posten? Um aus einer Zeichenkette zu lesen, müssen Sie in ein Byte-Array konvertieren, was einfach ist (getBytes)) ... und hier ist ein Blog-Beitrag, der Ihnen zeigt, wie man XML aus einem Array von Bytes analysiert ... https://xplpleware.wordpress.com/2016/06/02/parsefile-vs-parse-a-quick-comparison/ –

0

Alle Groovy betont:

import groovy.xml.XmlUtil 

def xml = '''<SchoolInfo RefId="34060F68BE3942F1B1264E6D2CC3C353"> 
    <LocalId>57</LocalId> 
    <SchoolName>Foobar School of Technology</SchoolName> 
    <Principal> 
     <FirstName>Bob</FirstName> 
     <LastName>Smith</LastName> 
    </Principal> 
    <StateProvinceId>34573</StateProvinceId> 
    <LEAInfoRefId>340666687E3942F1B1264E1223453C353</LEAInfoRefId> 
    <PhoneNumberList> 
     <PhoneNumber Type="0096"> 
      <Number>555-832-5555</Number> 
     </PhoneNumber> 
     <PhoneNumber Type="0096"> 
      <Number>555-999-5555</Number> 
     </PhoneNumber> 
    </PhoneNumberList> 
</SchoolInfo>''' 

def node = new XmlParser().parseText(xml) 

def whitelist = [ 'LocalId', 'SchoolName', 'Principal/FirstName', "PhoneNumberList/PhoneNumber/Number" ]*.split('/') 

def void loveRemovalMachine(node, whitelist) { 
    def elementNamesToKeep = whitelist*.head() 
    println "Retaining nodes ${elementNamesToKeep} for node $node" 
    def nodesToRemove = node.'*'.findAll { child -> !elementNamesToKeep.contains(child.name()) } 
    nodesToRemove.each { node.remove it } 
    def nextWhitelist = whitelist*.tail().findAll { it } 
    println "Next level: $nextWhitelist" 
    if (!nextWhitelist) { 
     return 
    } 
    // The "*" operator seems to return text nodes...very stupid. 
    node.'*:*'.each { loveRemovalMachine it, nextWhitelist } 
} 

loveRemovalMachine node, whitelist 

XmlUtil.serialize node 
+0

Ausgabe: '' 'Ergebnis: Foobar School of Technologie Bob Smith 555-832 -5555 555-999-5555 '' ' –

+0

Natürlich kann dies in Java getan werden; Bitte geben Sie an, wenn Sie ein solches Codebeispiel wünschen. Es ist die gleiche Idee: eine rekursive Methode, die das DOM aufräumt. Es wird nur viel mehr in Java sein. –