Jackson Xml: How to add namespace only on root? - java

If i declare the namespace on the root element, like this:
#JacksonXmlRootElement(namespace = "urn:stackify:jacksonxml", localName = "PersonData")
public class Person {
private String id;
private String name;
private String note;
}
It produces:
<PersonData xmlns="urn:stackify:jacksonxml">
<id xmlns="">12345</id>
<name xmlns="">Graham</name>
<note xmlns="">Hello</note>
</PersonData>
But I want the namespace only on the root element. The xmlns attribute should not appear on child elements.
How can i archive this?

There is a workaround which I found more elegant for me.
You may define constant for your namespace like this:
#JacksonXmlRootElement(localName = "PersonData")
public class Person {
#JacksonXmlProperty(isAttribute = true)
private final String xmlns = "urn:stackify:jacksonxml";
private String id;
private String name;
private String note;
}

You need to specify the same namespace as the root element in each attribute:
#JacksonXmlRootElement(namespace = "urn:stackify:jacksonxml", localName = "PersonData")
public class Person {
#JacksonXmlProperty(namespace = "urn:stackify:jacksonxml")
private String id;
#JacksonXmlProperty(namespace = "urn:stackify:jacksonxml")
private String name;
#JacksonXmlProperty(namespace = "urn:stackify:jacksonxml")
private String note;
}
Its a bit tiresome, but its the only way I found to avoid the unnecessary namespaces.

Also works well with immutables library and json annotations (if you need to serialize/deserialize both in JSON and in XML)
#Value.Immutable
#JsonRootName(value = "PersonData", namespace = "urn:stackify:jacksonxml")
public interface Person extends Serializable {
}

Related

Jackson parsing XML

I'm trying to serialize POJO class to Amazon XML format to aggregate the date from the service.
The goal is to have an xml like:
<ShipmentEventList>
<ShipmentEvent>
<ShipmentItemList>
<ShipmentItem></ShipmentItem>
</ShipmentItemList>
<AmazonOrderId>AAAA</AmazonOrderId>
<PostedDate>BBBB</PostedDate>
<MarketplaceName>CCCC</MarketplaceName>
<SellerOrderId>DDDD</SellerOrderId>
</ShipmentEvent>
</ShipmentEventList>
Here are my POJO classes
ShipmentEventList
public class ShipmentEventList {
#JacksonXmlElementWrapper(localName = "ShipmentEventList")
#JacksonXmlProperty(localName = "ShipmentEvent")
private List<ShipmentEvent> shipmentEventList;
}
ShipmentEvent
#JacksonXmlRootElement(localName = "ShipmentEvent")
public class ShipmentEvent {
#JacksonXmlElementWrapper(localName = "ShipmentItemList")
private List<ShipmentItem> shipmentItemList;
#JacksonXmlProperty(localName = "AmazonOrderId")
private String amazonOrderId;
#JacksonXmlProperty(localName = "PostedDate")
private String postedDate;
#JacksonXmlProperty(localName = "MarketplaceName")
private String marketplaceName;
#JacksonXmlProperty(localName = "SellerOrderId")
private String sellerOrderId;
}
Unfortunatelly, as a result of the serialization I have:
<ShipmentEventList>
<ShipmentEventList>
<ShipmentEvent>
<AmazonOrderId>A</AmazonOrderId>
<PostedDate>B</PostedDate>
<MarketplaceName>C</MarketplaceName>
<SellerOrderId>D</SellerOrderId>
</ShipmentEvent>
<ShipmentEvent>
<AmazonOrderId>B</AmazonOrderId>
<PostedDate>C</PostedDate>
<MarketplaceName>D</MarketplaceName>
<SellerOrderId>E</SellerOrderId>
</ShipmentEvent>
</ShipmentEventList>
</ShipmentEventList>
Could you explain me how does the serialization of collections work in Jackson?
You need to set useWrapping flag to false:
class ShipmentEventList {
#JacksonXmlElementWrapper(useWrapping = false)
#JacksonXmlProperty(localName = "ShipmentEvent")
private List<ShipmentEvent> shipmentEventList;
}

Reading xml file in Java - only selected elements

I have just started to learn how to make a java object from a XML. However, now I have a tricky input and I don't really know how to solve it.
This is the input:
<?xml version="1.0" encoding="UTF-8"?>
<return>
<productChange cd_product="711" ds_product="MBL40337 I151 BLACK P" cd_species="UN" cd_tipi="61062000" cd_cst="5" qt_weight="" cd_nbm="" dt="2016-05-04 11:47:36">
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="1" vl_product="61.39">
<tipovaluePrd tp_value="P" cd_value="1" ds_value="SALE" cd_corrency="1" />
</productvalue>
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="4" vl_product="129.8">
<tipovaluePrd tp_value="P" cd_value="4" ds_value="SALE STORE" cd_corrency="1" />
</productvalue>
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="5" vl_product="64.9">
<tipovaluePrd tp_value="P" cd_value="5" ds_value="SALE AT" cd_corrency="1" />
</productvalue>
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="8" vl_product="122.78">
<tipovaluePrd tp_value="P" cd_value="8" ds_value="SALE FQ" cd_corrency="1" />
</productvalue>
</productChange>
</return>
To consume this xml with JaxB (XML provided by a third party company) I have built the following classes:
#XmlAccessorType(XmlAccessType.FIELD)
public class ProductUpdateResponse {
#XmlAttribute(name = "cd_product")
private Integer productCode;
#XmlAttribute(name = "ds_product")
private String productDescription;
#XmlAttribute(name = "cd_species")
private String productSpecie;
#XmlAttribute(name = "cd_tipi")
private Integer productTIPI;
#XmlAttribute(name = "cd_cst")
private Integer productCST;
#XmlAttribute(name = "qt_weight")
private String productWeight;
#XmlAttribute(name = "cd_nbm")
private String productNBM;
#XmlAttribute(name = "dt")
private String productDate;
#XmlElement(name = "productvalue")
private ProductValueType productValue;
// getters and setters
}
#XmlAccessorType(XmlAccessType.FIELD)
public class ProductValue {
#XmlAttribute(name = "cd_product")
private Integer productCode;
#XmlAttribute(name = "cd_company")
private Integer companyCode;
#XmlAttribute(name = "tp_value")
private String valueType;
#XmlAttribute(name = "cd_value")
private Integer valueCode;
#XmlAttribute(name = "vl_product")
private BigDecimal productValue;
#XmlElement(name = "tipovaluePrd")
private ProductValueType productValueType;
//getters and seters
}
#XmlAccessorType(XmlAccessType.FIELD)
public class ProductValueType {
#XmlAttribute(name = "tp_value")
private String valueType;
#XmlAttribute(name = "cd_value")
private String valueCode;
#XmlAttribute(name = "ds_value")
private String valueDescription;
#XmlAttribute(name = "cd_corrency")
private Integer valueCurrency;
//getters and seters
}
However, the main information, for me, is comming as null (productvalue) I wonder if it is because there are four tags productvalue in the xml (I just need the one whose tipovaluePrd.cd_value is 4 ).
Below is the method I'm using to parse the XML:
private ProductUpdateResponse buildResponse(String rawResponse, Class<T> responseClass) {
JAXBContext jaxbContext = null;
try {
jaxbContext = JAXBContext.newInstance(responseClass);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(rawResponse);
return (ProductUpdateResponse) unmarshaller.unmarshal(reader);
} catch (JAXBException | ClassCastException e) {
return null;
}
}
So, I wonder if there is a way to read specifically the value I want (productvalue element that has tipovaluePrd.cd_value = 4) and how could I do that. If there isn't, how should I build my class so I can read properly everything?
Changing the input is not an option here...
Thanks in advance for any help.
I'm actually surprised you're getting a result from JAXB with your current setup.
You lack a class representing your root element :
#XmlRootElement(name="return")
#XmlAccessorType(XmlAccessType.FIELD)
public class Return {
#XmlElement(name="productchange")
private ProductUpdateResponse response;
//getters and setters
}
Also, in your ProductUpdateResponse class, you should change :
#XmlElement(name = "productvalue")
private ProductValueType productValue;
by
#XmlElement(name = "productvalue")
private List<ProductValue> productValues;
Finally, in your buildResponse method, make sure the responseClass argument is Return.class.
With this, you should have a list with all your productvalue element. You just have to get the productvalue your interested in from it.
If you only want to get the element you're interested with :
As swasa suggested it, you can use the javax.xml.xpath to make an Xpath request on your XML.
If you're willing to change your JAXB Implementation : EclipseLink JAXB Implementation implements an #XmlPath annotation that allows you to bind objects according to an XPath request :
#XmlPath(name = "productvalue/tipovaluePrd[#cd_value=4]")
private ProductValueType productValueType;

using JaxB to unmarshall object which contains List<String> field

I have a Java class which contains a List field.
public class Machine{
public int ID;
public String Name;
public String Status;
public String Environment;
public String Cluster;
public List<String> SupportedLocales;
}
Below is an instance of the class in XML that I am trying to unmarshall.
<?xml version="1.0" encoding="utf-8"?>
<Machine xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>27</ID>
<Name>QA14ADMINOPM201</Name>
<Status>Decom</Status>
<Environment>QA14</Environment>
<Cluster>ADMINOPM</Cluster>
<SupportedLocales>
<SupportedLocale>NA</SupportedLocale>
<SupportedLocale>Global</SupportedLocale>
</SupportedLocales>
</Machine>
When I unmarshall the Xml though, the resulting object has an empty SupportedLocales list, with no elements. Any reason why this might be happening or suggested fixes? All other fields are being unmarshalled correctly.
Annotate your field with an XmlElementWrapper annotation:
#XmlElementWrapper(name = "SupportedLocales")
#XmlElement(name = "SupportedLocale")
public List<String> SupportedLocales;
Also try to follow the convention of using variables that start with a lowercase (supportedLocales instead of SupportedLocales). This would need to map the XML element name for each field as follows:
#XmlElement(name="ID")
public int id;
#XmlElement(name="Name")
public String name;
#XmlElement(name="Status")
public String status;
#XmlElement(name="Environment")
public String environment;
#XmlElement(name="Cluster")
public String cluster;
#XmlElementWrapper(name = "SupportedLocales")
#XmlElement(name = "SupportedLocale")
public List<String> supportedLocales;

Using JAXB to create reference-objects accordingly to an attribute

Consider the following xml:
<Config>
<Paths>
<Path reference="WS_License"/>
</Paths>
<Steps>
<Step id="WS_License" title="License Agreement" />
</Steps>
</Config>
The following JAXB classes:
public class Path {
private String _reference;
public String getReference() {
return _reference;
}
#XmlAttribute
public void setReference( String reference ) {
_reference = reference;
}
}
And
public class Step {
private String _id;
private String _title;
public String getId() {
return _id;
}
#XmlAttribute
public void setId( String id ) {
_id = id;
}
public String getTitle() {
return _title;
}
#XmlAttribute
public void setTitle( String title ) {
_title = title;
}
}
Instead of storing the reference in the Path object as String, I'd like to hold it as a Step object. The link between those objects is the reference and id attributes. Is the #XMLJavaTypeAdapter attribute the way to go? Could anyone be so kind to provide an example of the correct usage?
Thanks!
EDIT:
I'd also would like to do the same technique with an element.
Consider the following xml:
<Config>
<Step id="WS_License" title="License Agreement">
<DetailPanelReference reference="DP_License" />
</Step>
<DetailPanels>
<DetalPanel id="DP_License" title="License Agreement" />
</DetailPanels>
</Config>
The following JAXB classes:
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute(name="id")
private String _id;
#XmlAttribute(name="title")
private String _title;
#XmlIDREF
#XmlElement(name="DetailPanelReference", type=DetailPanel.class)
private DetailPanel[] _detailPanels; //Doesn't seem to work
}
#XmlAccessorType(XmlAccessType.FIELD)
public class DetailPanel {
#XmlID
#XmlAttribute(name="id")
private String _id;
#XmlAttribute(name="title")
private String _title;
}
The property _detailPanels in the Step-object is empty and the link doesn't seems to work. Is there any option to create a link without creating a new JAXB object holding only the reference to the DetailPanel?
Thanks again : )!
You can use #XmlID to map a property as the key and #XmlIDREF to map the reference to the key for this use case.
Step
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute
private String _id;
}
Path
#XmlAccessorType(XmlAccessType.FIELD)
public class Path {
#XmlIDREF
#XmlAttribute
private Step _reference;
}
For More Information
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
UPDATE
Thanks! I Completely missed your article. I've extended my question,
do you have any clue if this is possible too? I do not want to create
a class with only holding the reference, I'd like to store it inside
the step class.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you are using MOXy as your JAXB (JSR-222) provider then you could leverage the #XmlPath annotation for your use case.
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute
private String id;
#XmlPath("DetailPanelReference/#reference")
#XmlIDREF
// private List<DetailPanel> _detailPanels; // WORKS
private DetailPanel[] _detailPanels; // See bug: http://bugs.eclipse.org/399293
}
For More Information
http://bugs.eclipse.org/399293
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html

Two #XmlJavaTypeAdapters for one #XmlAttribute in JAXB?

I have a class like this:
#XmlRootElement(name = "PricingGroup")
public class PricingGroup {
...
#XmlAttribute(name = "partyName")
#XmlJavaTypeAdapter(CustomerGroupRelationships.Adapter.class)
private List<BilltoCustomer> billtoCustomers = new ArrayList<BilltoCustomer>();
#XmlAttribute(name = "partyName")
#XmlJavaTypeAdapter(PartyNames.Adapter.class)
private PartyName partyName;
...
}
It seems JAXB can't map two #XmlJavaTypeAdapters for one attribute (here partyName). If I comment out either the annotations on billtoCustomers or the annotations on partyName, the other member variable is read from XML without problems.
How can I get both values at the same time?
You could map one of the properties (partyName) and then use an afterUnmarshal event to derive the other property (billToCustomers):
#XmlRootElement(name = "PricingGroup")
public class PricingGroup {
...
#XmlTransient
private List<BilltoCustomer> billtoCustomers = new ArrayList<BilltoCustomer>();
#XmlAttribute(name = "partyName")
#XmlJavaTypeAdapter(PartyNames.Adapter.class)
private PartyName partyName;
void afterUnmarshal(Unmarshaller u, Object parent) {
// Derive billToCustomers from partyName
}
...
}

Categories

Resources