Can we map XML like
<root>
<tagA>
<prop>111</prop>
<prop>222</prop>
<prop>333</prop>
<prop>444</prop>
</tagA>
<tagB>
<prop>555</prop>
<prop>666</prop>
</tagB>
<tagC>
<prop>777</prop>
</tagC>
</root>
to
class MyDto {
private List<TagA> tagAProps;
private List<TagB> tagBProps;
private List<TagC> tagCProps;
}
using JAXB? I am not sure how to use annotation to directly map elements to collection type. Straight mapping into POJO has five different classes (Root, TagA, TagB, TagC and Prop). Of which TagA, TagB and TagC being all similar. So I want to have this minimum number of class.
You can leverage the #XmlElementWrapper and #XmlElement annotations for this:
#XmlRootElement(name="root")
#XmlAccessorType(XmlAccessType.FIELD)
class MyDto {
#XmlElementWrapper(name="tagA")
#XmlElement(name="prop")
private List<String> tagAProps;
#XmlElementWrapper(name="tagB")
#XmlElement(name="prop")
private List<String> tagBProps;
#XmlElementWrapper(name="tagC")
#XmlElement(name="prop")
private List<String> tagCProps;
}
Related
I cant unmarshall xml because don't understand how to annotate object class in the another object. Please help.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<ODZ xmlns="http://www.company.com/1.0" >
<Data DataID="ZZZ">
<UserData UserKey="user_001">
<UserEvent>...</UserEvent>
</UserData>
</Data>
</ODZ>
Container classes:
I. First level with link to the second (ODZ -> Data).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "ODZ", namespace = "http://www.company.com/1.0")
public class ODZContainer {
private ImportContainer importContainer;
#XmlElement (name = "Data", type=ImportContainer.class)
public ImportContainer getImportContainer() {
return importContainer;
}
}
II. Second level with link to the third level(Data -> UserData).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "Data")
public class ImportContainer {
private String DataID;
private ArrayList<UserDataBean> userDataBean;
#XmlElement (name = "UserData", type=UserDataBean.class)
public ArrayList<UserDataBean> getUserDataBean() {
return userDataBean;
}
#XmlAttribute(name = "DataID")
public String getDataID() {
return DataID;
}
}
III. Third level with link to the fourth level(UserData-> UserEvent).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "UserData")
public class UserDataBean {
private ArrayList<UserEventBean> userEventData;
private String userEventID;
#XmlAttribute(name = "UserKey")
public String getUserEventID() {
return userEventID;
}
#XmlElement (name = "UserEvent", type=UserEventBean.class)
public ArrayList<UserEventBean> getUserEventBean() {
return userEventData;
}
}
The namespace qualification in your JAXB metadata does not match your XML. You can use the package level #XmlSchema annotation to specify the namespace qualification for your model.
#XmlSchema(
namespace = "http://www.company.com/1.0",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information on JAXB and Namespaces
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
Notes About Your Metadata
Since the type of the ArrayList is already specified, you don't need to specify it via the #XmlElement annotation. It doesn't hurt, but its not necessary.
#XmlElement (name = "UserData", type=UserDataBean.class)
public ArrayList<UserDataBean> getUserDataBean() {
return userDataBean;
}
#XmlAccessorType(XmlAccessType.NONE) means that nothing is mapped unless it is explicitly annotated. This may or not be what you want. You may find the following article useful:
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
I've requirement where I've to pass a parameter in the getter method(in my case another object). Wondering if thats possible and how can I achieve this? I'm implementing jaxws and this is my server side requirement. When client is created, getMetaValues(..) method is not available.
my code is
#XmlRootElement
#XmlAccessorType( XmlAccessType.PROPERTY)
public class Revision implements Serializable {
//other props and getter/setter
private List<Metavalue> metaValues;
#XmlElement
//Field is a custom class
public List<Metavalue> getMetaValues(Field field) {
metaValues=null;
if (field!=null) {
Map<String, Metakey> metadata = getMetadata();
Metakey metakey = metadata.get(field.getName());
if (metakey!=null) {
metaValues = metakey.getMetavalues();
}
}
return metaValues;
}
}
Thank you.
JAXB (JSR-222) implementations require a get method with no parameters. You could specify #XmlAccessorType(XmlAccessType.FIELD) so that JAXB deals directly with the field instead.
The class javax.security.auth.x500.X500Principal is final and (of course) not annotated with JAXB annotations? So how can I marshal it as part of a larger object graph? I would like to substitute it with the value of it's name property.
Edit
The solution is straightforward:
final class X500PrincipalXmlAdapter
extends XmlAdapter<String, X500Principal> {
#Override public X500Principal unmarshal(String name) {
return new X500Principal(name);
}
#Override public String marshal(X500Principal principal) {
return principal.getName();
}
}
Actually, I wonder why JAXB lets me get away with a non-public XMLAdapter class, but I certainly prefer it that way.
You could use an XmlAdapter for this use case. The XmlAdapter is used to convert an unmappable object into a mappable one. The XmlAdapter is configured on a field/property using the #XmlJavaTypeAdapter annotation.
Reflecting on JAXB annotated objects, is there a way to determine if a class/field/method will result in a xsi:type attributed during marshaling?
Is XmlElement annotation,
annotation.type != javax.xml.bind.annotation.XmlElement.DEFAULT.class
the only case I need to worry about?
I'm writing a Lua unmarshaler where we have dropped much of the usual xml type info and I'm trying to figure-out how to match the incoming Lua to JAXB.
Thanks.
--Update--
Here is simple example that shows the problem:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement()
#XmlSeeAlso({ Cat.class, Dog.class })
public class Animal {
#XmlElement()
public List<Animal> critters;
#XmlAttribute
public String type;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement()
public class Dog extends Animal {
public Dog() {
this.type = "German Shepherd";
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement()
public class Cat extends Animal {
public Cat() {
this.type = "Black";
}
}
When I receive an Animal object can I query critter's annotation to detect that it should be a Dog or Cat and not an Animal?
There are a couple circumstances where a JAXB (JSR-222) implementation will write out an xsi:type attribute.
If the field/property is of type Object (or is annotated with #XmlElement(type=Object.class)) and not mapped with #XmlAnyElement(lax=true) and holds an instance of an Object that the JAXBContext has mappings for.
The default mechanism for representing inheritance will result in an xsi:type attribute to represent subclasses (see: http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html).
I have my model:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class CustomerTest {
private Long id;
#XmlPath("contact-info/billing-address")
private AddressTest billingAddress;
#XmlPath("contact-info/shipping-address")
private AddressTest shippingAddress;
#XmlPath("FileHeader/SchemaVersion/text()")
private String schemaVersion;
}
And I fill in the object like this:
private void marshallCustomerTest() {
try {
JAXBContext jc = JAXBContext.newInstance(CustomerTest.class);
CustomerTest customer = new CustomerTest();
customer.setId(new Long(10));
customer.setSchemaVersion("3.2");
AddressTest billingAddress = new AddressTest();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
AddressTest shippingAddress = new AddressTest();
shippingAddress.setStreet("2 Shipping Road");
customer.setShippingAddress(shippingAddress);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(customer, System.out);
} catch (JAXBException jex) {
jex.printStackTrace();
log.error(jex);
}
}
This produce the next XML:
<customerTest xmlns:fe="http://www.facturae.es/Facturae/2009/v3.2/Facturae" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<id>10</id>
<contact-info>
<billing-address>
<street>1 Billing Street</street>
</billing-address>
<shipping-address>
<street>2 Shipping Road</street>
</shipping-address>
</contact-info>
<FileHeader>
<SchemaVersion>3.2</SchemaVersion>
</FileHeader>
</customerTest>
As you can see there is not #XmlPath annotation for 'id' property but this is also present in the final XML. I know I can avoid this behaviour setting the 'id' property to null but I want to know if there is another way. The point is that my real model is much bigger than this one and I would have to set a lot of properties to null.
Any help?
Thanks in advance.
You can either mark the property with #XmlTransient to have it excluded from the XML representation:
#XmlTransient
private Long id;
Or you can annotate your type with #XmlAccessorType(XmlAccessType.NONE) so that only annotated fields/properties are mapped.
#XmlAccessorType(XmlAccessType.NONE)
public class CustomerTest {
For More Information
http://blog.bdoughan.com/2012/04/jaxb-and-unmapped-properties.html