Marshall in geschachtelter Klasse

oraclin25

Erfahrenes Mitglied
Hallo zusammen,

ich darf zum Punkt gehen. Ich habe folgende Klassen:

Customer
Code:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement(name = "customer")
public class Customer {
	
	String name;
	InCustomer test;
	
	public String getName() {
		return name;
	}
 
	@XmlElement
	public void setName(String name) {
		this.name = name;
	}
	
	
	public InCustomer getTest(){
		return test;
	}
	
	@XmlElement
	public void setTest(InCustomer test){
		this.test = test; 
	}
	
}

InCustomer
Code:
import javax.xml.bind.annotation.XmlElement;

public class InCustomer {
	
	private String egal;
	
	public String getEgal() {
		return egal;
	}
 
	public void setEgal(String egal) {
		this.egal = egal;
	}

}

Und die main-Klasse:
Code:
import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class JAXBExample {
		public static void main(String[] args) {
	 
		 try {
	 
			File file = new File("D:\\file.xml");
			JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
	 
			Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
			Customer customer = (Customer) jaxbUnmarshaller.unmarshal(file);
			
			 Marshaller marshaller = jaxbContext.createMarshaller();
		        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		        marshaller.marshal(customer, System.out);
		  } catch (JAXBException e) {
			e.printStackTrace();
		  }
	 
		}
}

Mit folgendem test-XML:
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
	<name>ratna</name>
	<test>gogalop</test>
</customer>

Ich hätte eigentlich erwartet, dass mein Programm als Output, das gleiche XML ausspuckt, aber stattdessen sieht das Output so aus:
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <name>ratna</name>
    <test/>
</customer>

Was möchte ich hier erzielen? Wie man anhand der XML-Eingabe sieht, geht es mir um die 2 Unterlemente "name" und "test". Da "name" vom Typ String ist, ist es einfach, direkt in der Klasse Customer einzubinden. Ich dachte, statt String tue ich mal als Datentyp die Klasse InCustomer. InCustomer ist zwar ausgelagert, es muss aber möglich sein. Ich war vielleicht auch schon halbwegs auf dem richtigen Weg, aber leider nicht perfekt.

Könntet Ihr mir bitte Hilfestellungen geben? Vielen Dank.

Viele Grüße aus Rheinland,

Eure Ratna
 
Du müsstest ein XSD für InCustomer erstellen, da es einen Complex-Type darstellt. Anschließend musst du aus dem XSD die Java-Klassen generieren lassen. Und danach im XML den Namespace für InCustomer angeben (falls ein Namespace im XSD angegeben ist.

So mache ich es normalerweise.

XSD => ant generate => Java-Klassen => JAXB-(De-)Serialisierung
 
Hallo saftmeister,

danke für die Hilfestellung. Sie hat mir wahnsinnig viel geholfen, sie diente mir als Denkstoß und nachdem ichgelesen habe und mit ein bisschen Googlen gings schnell zu verstehen, wie der gängige Workaround bei dem Thema aussieht. Ich habe erfolgreich die notwendigen JARs heruntergeladen. Aus einer XSD habe ich erfolgreich Java-Klassen erzeugt. Nun stehen also die Java-Klassen.

Abeeer, mein eigentliches Problem ist ja eigentlich die Phase:
Java-Klassen --> Serialisierung

Ich muss nämlich ein XML-File auf die bereits erzeugten Java-Klassen abbilden. Es hat auch soweit geklappt. Dabei habe ich sehr viel gelernt, unter anderem folgendes:
Per Default erzeugt mir xjc den Zugriff an die Variablen @XmlAccessorType(XmlAccessType.FIELD)
Das heißt, ich muss FIELD durch PROPERTY ersetzen wenn ich @XmlElement auf getters schreiben möchte.

Wie gesagt, das Mapping von einem XML-Beispiel auf die Java-Klassen hat nur fast geklappt wegen dieser Kleinigkeit:
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "id"
this problem is related to the following location:
at public int Employee.getId()
at Employee
this problem is related to the following location:
at protected int Employee.id
at Employee

Der Code sieht wie folgt aus:
Code:
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name = "employee", propOrder = {
    "name",
    "salary",
    "designation",
    "address"
})
@XmlRootElement
public class Employee {

    protected String name;
    protected double salary;
    protected String designation;
    protected Address address;
    @XmlAttribute(required = true)
    protected int id;

...

@XmlElement
    public int getId() {
        return id;
    }

Das hat bestimmt mit dem @XmlAttribute-Annotation zu tun, denn bei allen anderen Variablen ohne das Pflicht-Attribut das Mapping funktioniert hat. Hast Du vielleicht eine Idee, was hier das Problem ist? Ich hab versucht die @XmlAttribute auf den Getter zu setzen, leider hat es auch nicht funktioniert:
Code:
    @XmlAttribute(required = true)
    @XmlElement
    public int getId() {
        return id;
    }

Hier war die Fehlermeldung:
Employee#id has mutually exclusive annotations @javax.xml.bind.annotation.XmlAttribute and @javax.xml.bind.annotation.XmlElement
this problem is related to the following location:
at @javax.xml.bind.annotation.XmlAttribute(name=##default, required=true, namespace=##default)
at public int Employee.getId()
at Employee
this problem is related to the following location:
at @javax.xml.bind.annotation.XmlElement(nillable=false, name=##default, required=false, type=class javax.xml.bind.annotation.XmlElement$DEFAULT, defaultValue= , namespace=##default)
at public int Employee.getId()

Ich habe versucht zu googeln, leider nicht schlau geworden. Vielen Dank für die Hilfestellungen.

Viele Grüße aus Rheinland,

Eure Ratna:)
 
Kannst du mal das XSD posten? Im Übrigen sollte man in generierten Klassen nachträglich nichts handisch rum pfriemeln. Das soll die Generierung erledigen - demzufolge passt das "Template" in Form des XSD noch nicht.
 
Hallo saftmeister,

das XSD ist relativ einfach. Wie man sieht, das Attribut id ist ein Pflichtattribut.
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
  <xs:element name="employee" type="employee"/>
 
  <xs:complexType name="address">
    <xs:sequence>
      <xs:element name="city" type="xs:string" minOccurs="0"/>
      <xs:element name="line1" type="xs:string" minOccurs="0"/>
      <xs:element name="line2" type="xs:string" minOccurs="0"/>
      <xs:element name="state" type="xs:string" minOccurs="0"/>
      <xs:element name="zipcode" type="xs:long"/>
    </xs:sequence>
  </xs:complexType>
 
  <xs:complexType name="employee">
    <xs:sequence>
      <xs:element name="name" type="xs:string" minOccurs="0"/>
      <xs:element name="salary" type="xs:double"/>
      <xs:element name="designation" type="xs:string" minOccurs="0"/>
      <xs:element name="address" type="address" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:int" use="required"/>
  </xs:complexType>
</xs:schema>

Viele Grüße aus Rheinland,

Eure Ratna
 
id sollte auch ein Element sein, daher verwende doch mal dieses XSD:

XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
  <xs:element name="employee" type="employee"/>
 
  <xs:complexType name="address">
    <xs:sequence>
      <xs:element name="city" type="xs:string" minOccurs="0"/>
      <xs:element name="line1" type="xs:string" minOccurs="0"/>
      <xs:element name="line2" type="xs:string" minOccurs="0"/>
      <xs:element name="state" type="xs:string" minOccurs="0"/>
      <xs:element name="zipcode" type="xs:long"/>
    </xs:sequence>
  </xs:complexType>
 
  <xs:complexType name="employee">
    <xs:sequence>
      <xs:element name="id">
        <xs:simpleType>
          <xs:restriction base="xs:integer">
            <xs:minInclusive value="1"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="name" type="xs:string" minOccurs="0"/>
      <xs:element name="salary" type="xs:double"/>
      <xs:element name="designation" type="xs:string" minOccurs="0"/>
      <xs:element name="address" type="address" minOccurs="0"/>
    </xs:sequence>
    <!--xs:attribute name="id" type="xs:int" use="required"/-->
  </xs:complexType>
</xs:schema>
 
Hallo saftmeister,

danke für die Antwort. Die von Dir vorgeschlagene XSD spuckt mir folgendes XML-Beispiel:
Code:
<?xml version="1.0" encoding="utf-8"?>
<employee>
  <id>1234</id>
  <name>str1234</name>
  <salary>3.1415926535</salary>
  <designation>str1234</designation>
  <address>
    <city>str1234</city>
    <line1>str1234</line1>
    <line2>str1234</line2>
    <state>str1234</state>
    <zipcode>12345</zipcode>
  </address>
</employee>

Die original XSD dagegen dieses:
Code:
<?xml version="1.0" encoding="utf-8"?>
<employee id="123">
  <name>str1234</name>
  <salary>3.1415926535</salary>
  <designation>str1234</designation>
  <address>
    <city>str1234</city>
    <line1>str1234</line1>
    <line2>str1234</line2>
    <state>str1234</state>
    <zipcode>12345</zipcode>
  </address>
</employee>

Id ist also ein Attribut vom Root-Element. Da diese Konstellation, soweit ich weiss, relativ häufig vorkommt, möchte ich gerne auf Basis der originalen XSD arbeiten.

Ich kann ja mal zeigen, wie Employee.java mit Hilfe des xjc-Tools erzeugt, aussieht :
Code:
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-520 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2014.01.19 at 02:06:12 AM MEZ 
//


package com.theopentutorials.jaxb.beans;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for employee complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="employee">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="salary" type="{http://www.w3.org/2001/XMLSchema}double"/>
 *         &lt;element name="designation" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="address" type="{}address" minOccurs="0"/>
 *       &lt;/sequence>
 *       &lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "employee", propOrder = {
    "name",
    "salary",
    "designation",
    "address"
})
public class Employee {

    protected String name;
    protected double salary;
    protected String designation;
    protected Address address;
    @XmlAttribute(required = true)
    protected int id;

    /**
     * Gets the value of the name property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getName() {
        return name;
    }

    /**
     * Sets the value of the name property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setName(String value) {
        this.name = value;
    }

    /**
     * Gets the value of the salary property.
     * 
     */
    public double getSalary() {
        return salary;
    }

    /**
     * Sets the value of the salary property.
     * 
     */
    public void setSalary(double value) {
        this.salary = value;
    }

    /**
     * Gets the value of the designation property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getDesignation() {
        return designation;
    }

    /**
     * Sets the value of the designation property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setDesignation(String value) {
        this.designation = value;
    }

    /**
     * Gets the value of the address property.
     * 
     * @return
     *     possible object is
     *     {@link Address }
     *     
     */
    public Address getAddress() {
        return address;
    }

    /**
     * Sets the value of the address property.
     * 
     * @param value
     *     allowed object is
     *     {@link Address }
     *     
     */
    public void setAddress(Address value) {
        this.address = value;
    }

    /**
     * Gets the value of the id property.
     * 
     */
    public int getId() {
        return id;
    }

    /**
     * Sets the value of the id property.
     * 
     */
    public void setId(int value) {
        this.id = value;
    }

}

Und da ich gerne aus einem XML-Dokument in ein Java-Objekt serialisieren möchte, habe ich JAXB-Annotationen auf die Klasse gepackt, leider ist mir noch nicht ganz gelungen. Und ich meine, es liegt an diesem id-Attribut.

Code:
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vhudson-jaxb-ri-2.1-520 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2014.01.19 at 02:06:12 AM MEZ 
//



import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * <p>Java class for employee complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="employee">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="salary" type="{http://www.w3.org/2001/XMLSchema}double"/>
 *         &lt;element name="designation" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="address" type="{}address" minOccurs="0"/>
 *       &lt;/sequence>
 *       &lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name = "employee", propOrder = {
    "name",
    "salary",
    "designation",
    "address"
})
@XmlRootElement
public class Employee {

    protected String name;
    protected double salary;
    protected String designation;
    protected Address address;
    @XmlAttribute(required = true)
    protected int id;

    /**
     * Gets the value of the name property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    @XmlElement
    public String getName() {
        return name;
    }

    /**
     * Sets the value of the name property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setName(String value) {
        this.name = value;
    }

    /**
     * Gets the value of the salary property.
     * 
     */
    @XmlElement
    public double getSalary() {
        return salary;
    }

    /**
     * Sets the value of the salary property.
     * 
     */
    public void setSalary(double value) {
        this.salary = value;
    }

    /**
     * Gets the value of the designation property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    @XmlElement
    public String getDesignation() {
        return designation;
    }

    /**
     * Sets the value of the designation property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setDesignation(String value) {
        this.designation = value;
    }

    /**
     * Gets the value of the address property.
     * 
     * @return
     *     possible object is
     *     {@link Address }
     *     
     */
    @XmlElement
    public Address getAddress() {
        return address;
    }

    /**
     * Sets the value of the address property.
     * 
     * @param value
     *     allowed object is
     *     {@link Address }
     *     
     */
    public void setAddress(Address value) {
        this.address = value;
    }

    /**
     * Gets the value of the id property.
     * 
     */
    
    
    @XmlElement
    public int getId() {
        return id;
    }

    /**
     * Sets the value of the id property.
     * 
     */
    public void setId(int value) {
        this.id = value;
    }

}

Ich habe lediglich den AccessType von FIELD auf PROPERTY umgestellt. Desweiteren habe ich @XmlRootelement und @XmlElement auf die entsprechenden getters gepackt. Bis auf das Attribut id hat alles funktioniert.:(

Class has two properties of the same name "id"
this problem is related to the following location:
at public int Employee.getId()
at Employee
this problem is related to the following location:
at protected int Employee.id
at Employee

Komisch oder? Hast Du vielleicht eine Idee, warum es beim Attribut id hackt? Danke schön schon mal..

Viele Grüße aus Rheinland,

Eure Ratna:)
 
Zurück