Search This Blog

Thursday 16 May 2013

More on Complex Types

In the last post we were able to map complex xml elements to classes. However it is recommended to build complex types and associate them to elements.
We saw a similar thing in the example of simple elements.
  1.  Define a top level type.
  2. Define elements of the specific type.
I modified the previous xsd accordingly:
<element name="attributedAddress2" type="si:attributedAddressType" />

<complexType name="attributedAddressType">
 <attribute name="city" type="string" use="optional" default="-" />
 <attribute name="zipcode" type="integer" use="required" />
 <attribute name="country" type="string" fixed="India" />
</complexType>
The code generated by XJC in this case is as below:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "attributedAddressType")
public class AttributedAddressType {

 @XmlAttribute
 protected String city;
 @XmlAttribute(required = true)
 protected BigInteger zipcode;
 @XmlAttribute
 protected String country;
 // same code as the previous example - setter getters

}
Here JAXB generated a class for the complex type. Hence we do not see the XmlRootElement annotation here.
For marshaling we need to create elements of type attributedAddress2. This is shown in the code for ObjectFactory:
private final static QName _AttributedAddress2_QNAME = new 
        QName("http://test.com/xsd/simple/01/", "attributedAddress2");

@XmlElementDecl(namespace = "http://test.com/xsd/simple/01/", name = "attributedAddress2")
public JAXBElement<AttributedAddressType> createAttributedAddress2(
 AttributedAddressType value) {
 return new JAXBElement<AttributedAddressType>(
  _AttributedAddress2_QNAME, AttributedAddressType.class, null,
  value);
}

public AttributedAddressType createAttributedAddressType() {
        return new AttributedAddressType();
}
To get the xml representation :
static void testMarshal() throws JAXBException {
 final ObjectFactory objectFactory = new ObjectFactory();
 AttributedAddressType addressType = objectFactory
   .createAttributedAddressType();
 addressType.setCity("Pune");
 addressType.setZipcode(BigInteger.valueOf(411020));

 JAXBElement<AttributedAddressType> address2 = objectFactory
   .createAttributedAddress2(addressType);
 final JAXBContext jContext = JAXBContext
   .newInstance(AttributedAddressType.class);

 final Marshaller marshaller = jContext.createMarshaller();
 final StringWriter stringWriter = new StringWriter();
 marshaller.marshal(address2, stringWriter);

 System.out.println(stringWriter.toString());
}
And the output is :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<attributedAddress2 zipcode="411020" city="Pune"
 xmlns="http://test.com/xsd/simple/01/" />
To convert back this XML to java:
static void testUnmarshal() throws JAXBException {
 final JAXBContext jContext = JAXBContext
   .newInstance(AttributedAddressType.class);
 final Unmarshaller unmarshaller = jContext.createUnmarshaller();
 
 final StreamSource source = new StreamSource(
  TestComplexElements.class
   .getResourceAsStream("/AttributedAddress2.xml"));
 AttributedAddressType address1 = unmarshaller.unmarshal(source,
  AttributedAddressType.class).getValue();
  
 System.out.println(address1.getCity() + ", " + address1.getCountry()
  + " - " + address1.getZipcode());
}
Some might find this absence of class an irritant. Working with JAXBElement is not the only way here. For this we need to define the xsd as below:
<element name="addressInstance">
 <complexType>
  <sequence>
   <element name="address" type="si:attributedAddressType" />
  </sequence>
 </complexType>
</element>
The corresponding class generated by XJC is :
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"address"})
@XmlRootElement(name = "addressInstance")
public class AddressInstance {

 @XmlElement(required = true)
 protected AttributedAddressType address;

 // setters and getters

}

1 comment: