Jackson Serialization of XML with multiple attributes - java

Trying to figure out how to serialize an xml payload with jackson that has multiple attributes(?) as well as namespaces or locals.
I need the payload to look like this:
<wfs:GetFeature xmlns:wfs="http://www.opengis.net/wfs"
xmlns:ogc="http://www.opengis.net/ogc" xmlns:fade="http://www.bit-sys.com/fade" service="WFS" version="1.1.0" resultType="results" maxFeatures="150001" outputFormat="application/json">
<wfs:Query srsName="EPSG:4326" typeName="ExampleTypeName">
<Filter xmlns="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml">
...
And I am working on creating a pojo to use to map the data necessary for this payload to the correct location, but just having trouble differentiating the namespace vs localName parts. The pojo I have so far isn't really producing the desired result (even for the first <GetFeature /> part.
public class MyXmlObject {
#JacksonXmlRootElement(localName = "wfs", namespace = "wfs")
class GetFeature {
#JacksonXmlProperty(isAttribute = true)
private String wfs = "http://www.opengis.net/wfs";
#JacksonXmlProperty(isAttribute = true)
private String ocg = "http://www.opengis.net/ogc";
#JacksonXmlProperty(isAttribute = true)
private String fade = "http://www.bit-sys.com/fade";
#JacksonXmlProperty(isAttribute = true)
private String service;
#JacksonXmlRootElement(localName = "wfs")
public class Query {
#JacksonXmlProperty(isAttribute = true)
private String srsName = "exampleSrsName";
#JacksonXmlProperty(isAttribute = true)
private String typeName = "exampleTypeName";
}
}
}
Right now only running a simple test to see how my xml payload looks as I work on it produces <MyXmlObject /> with nothing else in it, not too sure what I am doing wrong.
#Test
public void whenJavaSerializedToXmlStr_thenCorrect()
throws JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
String xml = xmlMapper.writeValueAsString(new MistXmlObject());
System.out.print(xml);
}

Related

Jackson XML - Deserialize from specific node

I'm new to Jackson XML and I would like to do the following:
Here is the XML I'd like to convert to Java DTO:
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<cotacaoTaxaCambioResponse
xmlns="http://tempuri.org/">
<cotacaoTaxaCambioResult
xmlns:a="http://schemas.datacontract.org/2004/07/Exchange"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:TaxasCambio>
<a:CotacaoTaxaCambio>
<a:CLIENTEC>0</a:CLIENTEC>
<a:CLIENTEV>0</a:CLIENTEV>
<a:COMERCIALC>0</a:COMERCIALC>
<a:COMERCIALV>30</a:COMERCIALV>
<a:DATA>2021-12-15T00:00:00</a:DATA>
<a:HORARIO>1027</a:HORARIO>
<a:MOEDA>978</a:MOEDA>
<a:PARIDADEC>1</a:PARIDADEC>
<a:PARIDADEV>1</a:PARIDADEV>
<a:TURISMOC>0</a:TURISMOC>
<a:TURISMOV>30</a:TURISMOV>
</a:CotacaoTaxaCambio>
</a:TaxasCambio>
<a:serviceStatus>
<a:CODRETORNO>0</a:CODRETORNO>
<a:MENSAGEM>Sucesso</a:MENSAGEM>
<a:MENSAGEMEN>OK</a:MENSAGEMEN>
<a:NRREFERENCE>0</a:NRREFERENCE>
</a:serviceStatus>
</cotacaoTaxaCambioResult>
</cotacaoTaxaCambioResponse>
</s:Body>
</s:Envelope>
Info I need is attributes of a:CotacaoTaxaCambio node.
Created this DTO
#JacksonXmlRootElement(localName = "a:CotacaoTaxaCambio")
public class CotacaoResDTO {
#JacksonXmlProperty(localName = "a:DATA")
private String data;
#JacksonXmlProperty(localName = "a:HORARIO")
private String hora;
#JacksonXmlProperty(localName = "a:MOEDA")
private String codMoeda;
#JacksonXmlProperty(localName = "a:COMERCIALV")
private Double valorComercial;
#JacksonXmlProperty(localName = "a:TURISMOV")
private Double valorTurismo;
}
If I set this subset as input, it does work:
<a:CotacaoTaxaCambio>
<a:CLIENTEC>0</a:CLIENTEC>
<a:CLIENTEV>0</a:CLIENTEV>
<a:COMERCIALC>0</a:COMERCIALC>
<a:COMERCIALV>30</a:COMERCIALV>
<a:DATA>2021-12-15T00:00:00</a:DATA>
<a:HORARIO>1027</a:HORARIO>
<a:MOEDA>978</a:MOEDA>
<a:PARIDADEC>1</a:PARIDADEC>
<a:PARIDADEV>1</a:PARIDADEV>
<a:TURISMOC>0</a:TURISMOC>
<a:TURISMOV>30</a:TURISMOV>
</a:CotacaoTaxaCambio>
But it doesn't deserialize if I set full xml as input.
Is there any configuration or anotation that I can use to archieve this?
Thanks in advance,
Unfortunately at the moment the jackson library does not support directly the SOAP protocol, but you can read the part you are interested with the XMLStreamReader class directly pointing the involved tag:
XMLInputFactory f = XMLInputFactory.newFactory();
XMLStreamReader sr = f.createXMLStreamReader(new FileInputStream(xml));
XmlMapper mapper = new XmlMapper();
sr.nextTag();
while (!sr.getLocalName().equals("CotacaoTaxaCambio")) {
sr.nextTag();
}
CotacaoResDTO value = mapper.readValue(sr, CotacaoResDTO.class);
sr.close();
You have to modify also your CotacaoResDTO class ignoring the unknown properties :
#JsonIgnoreProperties(ignoreUnknown = true)
public class CotacaoResDTO {
#JacksonXmlProperty(localName = "DATA")
private String data;
#JacksonXmlProperty(localName = "HORARIO")
private String hora;
#JacksonXmlProperty(localName = "MOEDA")
private String codMoeda;
#JacksonXmlProperty(localName = "COMERCIALV")
private Double valorComercial;
#JacksonXmlProperty(localName = "TURISMOV")
private Double valorTurismo;
}

How to (de)serialize immutable object with custom name for list name?

I'm trying to serialize and deserialize object with Jackson into XML, however I'm heaving troubles to do so...
My object:
Is an immutable class with getters and constructor only
It contains a list of sub-objects messages
I want to serialize this list wrapped in element <messages> and each message as a <message> element - as I understood I have to place #JsonProperty("message") #JacksonXmlElementWrapper(localName = "messages") annotations on getters, since when I place it on a constructor, it does not produce desired output (both are the same <message(s)>)
<response id="response-id">
<messages>
<message code="a"/>
<message code="b"/>
</messages>
</response>
I need to deserialize this structure, however as it's immutable object with final variables, I have to deserialize it through constructor.
Code snippet:
public class Playground {
public static void main(String[] args) throws JsonProcessingException {
Response response = new Response("response-id", List.of(new Message("a"), new Message("b")));
XmlMapper xmlObjectMapper = new XmlMapper(new XmlFactory());
xmlObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
final String value = xmlObjectMapper.writeValueAsString(response);
System.out.println(value);
assert ("<response id=\"response-id\"><messages><message code=\"a\"/><message "
+ "code=\"b\"/></messages></response>")
.equals(value) : "Desired format does not match!";
final Response deserializedValue = xmlObjectMapper.readValue(value, Response.class);
//final String deserialized = xmlObjectMapper.writeValueAsString(deserializedValue);
//assert value.equals(deserialized) : "Does not match";
}
}
#JsonInclude(Include.NON_EMPTY)
#JacksonXmlRootElement(localName = "response")
#JsonPropertyOrder({"id", "messages"})
class Response {
private final String id;
private final List<Message> messages;
#JsonCreator
public Response(
#JacksonXmlProperty(localName = "id", isAttribute = true) final String id,
#JsonProperty("message") final List<Message> messages) {
this.id = id;
this.messages = messages;
}
#JacksonXmlProperty(localName = "id", isAttribute = true)
public String getId() {
return id;
}
#JsonProperty("message")
#JacksonXmlElementWrapper(localName = "messages")
public List<Message> getMessages() {
return messages;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_EMPTY)
class Message {
private final String code;
public Message(#JacksonXmlProperty(localName = "code", isAttribute = true) final String code) {
this.code = code;
}
#JacksonXmlProperty(localName = "code", isAttribute = true)
public String getCode() {
return code;
}
}
When I try to deserialize it without messages annotated in the constructor I get exception: Invalid type definition for type 'Response': Argument #1 of constructor [constructor for 'Response' (2 args), annotations: {interface JsonCreator=#JsonCreator(mode=DEFAULT)} has no property name (and is not Injectable): can not use as property-based Creator
When I add #JsonProperty("message") annotation, I get exception: Invalid definition for property 'messages' (of type 'Response'): Could not find creator property with name 'messages' (known Creator properties: [id, message])
When I change it to #JsonProperty("messages") (plural), I get: Duplicate property 'messages' for [simple type, class Response]
When I add both, #JsonProperty("message") #JacksonXmlElementWrapper(localName = "messages"), it produces exception: Invalid definition for property 'messages' (of type 'Response'): Could not find creator property with name 'messages' (known Creator properties: [id, message]). (Nor any combination of message/messages in those two annotations works)
What am I doing wrong? What annotations do I need to use to retrieve desired XML output with it's deserialization?
For future googlers:
This seems to be related to jackson-dataformat-xml issue:
https://github.com/FasterXML/jackson-dataformat-xml/issues/187
There is also explanation and a workaround posted.

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;
}

JAXB - how to unmarshal an entity into list of entities with size 1

I have the following JAXB entity:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = EntityConstants.PARTNER)
public class FilePartner
{
#XmlAttribute(name = EntityConstants.IDENTIFIER, required = true)
private String identifier;
#XmlElement(name = EntityConstants.NAME)
private String name;
#XmlElement(name = EntityConstants.ROOT_PATH)
private String rootPath;
...
}
which serialized into a similar structure:
<file-partner identifier="foo">
<name>bar</name>
<root-path>path123</root-path>
...
</file-partner>
I also have an entity which represents a list of partners:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = EntityConstants.PARTNERS)
public class FilePartnerList
{
#XmlElement(name = EntityConstants.PARTNER)
private List<FilePartner> partners = new ArrayList<FilePartner>();
public List<FilePartner> getPartners()
{
return partners;
}
public void addPartners(List<FilePartner> partners)
{
this.partners.addAll(partners);
}
}
which serializes into:
<partners>
<file-partner identifier="foo">
...
</file-partner>
<file-partner identifier="foo2">
...
</file-partner>
...
</partners>
I am looking for a way to force the jaxb unmarshaller to deserialize XMLs in the form of
<file-partner identifier="foo">
<name>bar</name>
<root-path>path123</root-path>
...
</file-partner>
into FilePartnerList instances with list size of 1, i.e:
JAXBContext context = JAXBContext.newInstance(FilePartner.class, FilePartnerList.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream inputStream = getResourceAsStream(filePartnerAsXml);
FilePartnerList partnerList = (FilePartnerList) unmarshaller.unmarshal(inputStream); // This SHOULD be unmarshalled to FilePartnerList instead of FilePartner
assertTrue(partnerList.getPartners().getSize().equals(1));
How do I achieve that?
private List<FilePartner> partners = new ArrayList<FilePartner>(**1**);
That way you'll get a fixed size input array....
But I'm sure that you will want to get the XSD of that model with the 'maxocurrs=1' so you will end modifying the XSD manually.
Anyway: why don't you, if the list size must be fixed to '1', simply set it as a simple node with a single child? Something like this (untested):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = EntityConstants.PARTNERS)
public class FilePartnerList
{
#XmlElement(name = EntityConstants.PARTNER)
private FilePartner partners;
public FilePartner getFilePartner()
{
return partner;
}
public void setPartner(FilePartner partner)
{
this.partner = partner;
}
}
This way you will have one and only one partner per parnets-list.
The XML that fullfits the XSD of your service a text and in that text is indistinguible a list with max size 1 and a node.

Using jackson xml mapper to map java list in xml

I've a problem with xml Mapper.
I'm using the com.fasterxml.jackson.xml.XmlMapper library.
Practically, this is my java bean that map the values in this way:
#JsonAutoDetect
#JacksonXmlRootElement(localName ="Patient")
public class Patient implements Serializable {
private static final long serialVersionUID = -2981849269841429849L;
#JsonProperty("patientId")
#JacksonXmlProperty(isAttribute = true)
private String patientId;
#JsonProperty("patientName")
#JacksonXmlProperty(isAttribute = true)
private String patientName;
#JacksonXmlProperty(localName = "Series")
#JacksonXmlElementWrapper(useWrapping=false)
private ArrayList<Serie> listSerie;
}
and generate in the main class the xml with:
Wado mapp = new Wado();
mapp.setvalue("bla bla");
String xmlWado = new XmlMapper().writeValueAsString(wado);
the result is:
<Patient patientId="" patientName="">
<Series>
<Series></Series>
<Series></Series>
<Series></Series>
</Series>
that's not I was expected. I want:
<Patient patientId="" patientName="">
<Series></Series>
<Series></Series>
<Series></Series>
Any suggestions?

Categories

Resources