2016-05-10 12 views
0

Ich versuche, die folgenden "AttrName" und "AttrType" XML-Elemente in einer einzigen Klasse zu marshalieren und zu entpacken. (Im Moment lese ich die Werte individuell und baue meine Objekte nach dem unmarshalling in Java.)JAXB Marshaling und Unmarshalling mehrerer XML-Elemente in eine einzige Klasse und umgekehrte

<wrapper> 
    <someOtherElement>xxx</someOtherElement> 
    <attrName ref="a">xxx</attrName> 
    <attrName ref="b">xxx</attrName> 
    <attrName ref="c">xxx</attrName> 
    <attrType attrRef="a">xxx</attrType> 
    <attrType attrRef="b">xxx</attrType> 
    <someOtherElement>xxx</someOtherElement> 
</wrapper> 

Das „ref“ XML-Attribut verwendet wird, ein Attribut zu identifizieren und als Referenz für die „attrType“ XML -Element. Aber das XML-Element "attrType" ist optional und muss nicht vorhanden sein. Es darf kein "attrType" XML-Element ohne ein "attrName" XML-Element geben.

Ich brauche eine Liste der „attribute“ Objekte der Klasse zu generieren:

package example; 

public class Attribute { 

    private String name; 

    private String ref; 

    private String type; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name= name; 
    } 

    public String getRef() { 
     return ref; 
    } 

    public void setRef(String ref) { 
     this.ref= ref; 
    } 

    public String getType() { 
     return type; 
    } 

    public void setType(String type) { 
     this.type= type; 
    } 
} 

ich bereits folgenden Zusammenhang question gefunden. Aber es hat mir nicht geholfen, eine Lösung für mein Problem zu finden. Das Problem besteht darin, alle zugehörigen Attributnamen und -typen zu finden, um ein Java-Objekt zu erstellen.

Ich wäre dankbar für alle Tipps oder Ratschläge in die richtige Richtung. Wenn ich nichts zufriedenstellendes erklärt habe, zögere bitte nicht zu fragen, da Englisch nicht meine Muttersprache ist.

PS: Ich weiß, ich könnte eine andere XML-Struktur verwenden und das Problem einfach lösen. Aber das ist nicht möglich für mich.

+0

Um zu klären. Was ich gerne wissen würde ist, wie man die "Attribut" -Klasse (und/oder Hilfsklassen) annotiert, um das XML-Schema für den beschriebenen Fall zu beschreiben. – Jonas

Antwort

0

Ihre XML-Datei folgt keinem Schema, daher können Sie sich nur auf den Namen des Stammelements und die Tatsache verlassen, dass es wohlgeformt ist. Die folgenden Karten sie zu einem generischen Wrapper-Objekt, und verarbeitet sie die Liste der Attribute Objekte erzeugen:

public class JAXBAttribute { 

    public static void main(String[] args) throws JAXBException { 
     JAXBContext context = JAXBContext.newInstance(new Class[] {Wrapper.class}); 
     Wrapper wrapper = (Wrapper)context.createUnmarshaller().unmarshal(Thread.currentThread().getContextClassLoader().getResourceAsStream("wrapper.xml")); 
     final Map<String,String> attributeTypeMap = new HashMap<String,String>(); 
     for(final AttributeTypeMapEntry entry : wrapper.getEntryList()) { 
      attributeTypeMap.put(entry.getKey(), entry.getValue()); 
     } 
     for(final Attribute a : wrapper.getAttributeObjectList()) { 
      a.setType(attributeTypeMap.get(a.getRef())); 
     } 
     System.out.println(wrapper.getAttributeObjectList()); 
    } 

} 

@XmlRootElement(name="wrapper") 
@XmlAccessorType(XmlAccessType.NONE) 
class Wrapper { 

    @XmlElement(name="attrName") 
    private List<Attribute> attributeObjectList; 

    @XmlElement(name="attrType") 
    private List<AttributeTypeMapEntry> entryList; 

    public List<Attribute> getAttributeObjectList() { 
     return attributeObjectList; 
    } 

    public List<AttributeTypeMapEntry> getEntryList() { 
     return entryList; 
    } 
} 

@XmlAccessorType(XmlAccessType.NONE) // Only annotated fields will be mapped 
class AttributeTypeMapEntry { 

    @XmlAttribute(name="attrRef") 
    private String key; 

    @XmlValue 
    private String value; 

    public String getKey() { 
     return key; 
    } 

    public void setKey(String key) { 
     this.key = key; 
    } 

    public String getValue() { 
     return value; 
    } 

    public void setValue(String value) { 
     this.value = value; 
    } 

    @Override 
    public String toString() { 
     return "AttributeTypeMapEntry [key=" + key + ", value=" + value + "]"; 
    } 
} 

@XmlAccessorType(XmlAccessType.NONE) // Only annotated fields will be mapped 
class Attribute { 

    @XmlValue 
    private String name; 

    private String type; 

    @XmlAttribute(name="ref") 
    private String ref; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name= name; 
    } 

    public String getRef() { 
     return ref; 
    } 

    public void setRef(String ref) { 
     this.ref= ref; 
    } 

    public String getType() { 
     return type; 
    } 

    public void setType(String type) { 
     this.type= type; 
    } 

    @Override 
    public String toString() { 
     return "Attribute [name=" + name + ", type=" + type + ", ref=" + ref + "]"; 
    } 
} 
+0

Vielen Dank für die gründliche Antwort. Wie im ersten Absatz in Klammern gesagt, ist das ähnlich dem, was ich gerade mache. Mein wirklicher Anwendungsfall des XML ist viel komplexer. Dies ist nur ein kleiner Ausschnitt zur Verdeutlichung. Gibt es wirklich keinen anderen Weg, JAXB die Arbeit machen zu lassen? – Jonas

+0

Um die JAXB Mapping-Fähigkeit zu nutzen, müssen Sie das Schema Ihres XML kennen. Ansonsten tust du es auf generische Weise. – dimplex

+0

OK, ich denke ich weiß was du meinst. Ich verwende Java-Annotationen für die XML-Schemazuordnung. Und was ich gerne wissen würde, ist, wie man die Klasse (und/oder Hilfsklassen) annotiert, um das XML-Schema für den beschriebenen Fall zu beschreiben. – Jonas

0

Dies ist eine weitere Lösung für die gleiche Aufgabe. Die Idee besteht darin, das ursprüngliche Dokument in ein XML-Formular umzuwandeln, wobei alle Referenzen aufgelöst werden und das resultierende XML Java-Objekten zugeordnet wird.

Die XSLT für Ihre Daten wären:

<?xml version="1.0" encoding="utf-8"?> 
<wrapper> 
    <attribute> 
     <name>xxx</name> 
     <ref>a</ref> 
     <type>xxx</type> 
    </attribute> 
    <attribute> 
     <name>xxx</name> 
     <ref>b</ref> 
     <type>xxx</type> 
    </attribute> 
    <attribute> 
     <name>xxx</name> 
     <ref>c</ref> 
     <type/> 
    </attribute> 
</wrapper> 

Der Code mit Anmerkungen würde wie folgt aussehen:

<?xml version="1.0" encoding="UTF-8" ?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 

    <xsl:template match="/wrapper"> 
     <xsl:variable name="root" select="."/> 
     <xsl:copy> 
      <xsl:for-each select="attrName"> 
       <xsl:variable name="ref" select="@ref"></xsl:variable> 
       <attribute> 
        <name><xsl:value-of select="text()"/></name> 
        <ref><xsl:value-of select="$ref"/></ref> 
        <type><xsl:value-of select="$root/attrType[@attrRef=$ref]/text()"/></type> 
       </attribute> 
      </xsl:for-each> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="text()"/> 
</xsl:stylesheet> 

Die XML-straight-to-Karte aussehen würde

public class JAXBAttribute { 

    public static void main(String[] args) throws Exception { 
     // Transform initial XML resolving all references, resulting in an straight-to-map XML 
     final Transformer t = TransformerFactory.newInstance().newTransformer(
      new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream("wrapper.xsl"))); 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
     t.transform(
      new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream("wrapper.xml")), 
      new StreamResult(baos)); 
     // Create Java objects from a straight-to-map XML 
     JAXBContext context = JAXBContext.newInstance(new Class[] {Wrapper.class}); 
     Wrapper wrapper = (Wrapper)context.createUnmarshaller().unmarshal(new ByteArrayInputStream(baos.toByteArray())); 
     // 
     System.out.println(wrapper.getAttributeObjectList()); 
    } 

} 

@XmlRootElement(name="wrapper") 
@XmlAccessorType(XmlAccessType.FIELD) 
class Wrapper { 

    @XmlElement(name="attribute") 
    private List<Attribute> attributeObjectList; 

    public List<Attribute> getAttributeObjectList() { 
     return attributeObjectList; 
    } 
} 

@XmlAccessorType(XmlAccessType.FIELD) 
class Attribute { 

    private String name; 

    private String type; 

    private String ref; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name= name; 
    } 

    public String getRef() { 
     return ref; 
    } 

    public void setRef(String ref) { 
     this.ref= ref; 
    } 

    public String getType() { 
     return type; 
    } 

    public void setType(String type) { 
     this.type= type; 
    } 

    @Override 
    public String toString() { 
     return "Attribute [name=" + name + ", type=" + type + ", ref=" + ref + "]"; 
    } 
} 

Beachten Sie, dass alle Dateien für dieses Beispiel im Stammverzeichnis des Klassenpfads verbleiben.

+0

Vielen Dank für die Mühe. Es ist sehr geschätzt! Ich werde mir die Lösung später etwas genauer ansehen, da ich im Moment eine andere Aufgabe habe. – Jonas