Jackson parsing XML - java

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

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

unable to parse CSV with `CsvParser` abstract class mapping but works with JSON mapper

Unable to parse CSV files to Object type,
CsvMapper not working with polymorphism, while JSON mapper works with jsonString.
#Getter
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
property = "type", visible = true,
include = JsonTypeInfo.As.PROPERTY)
#JsonSubTypes({
#Type(value = FHR.class, name = "FHR"),
#Type(value = BHR.class, name = "BHR")})
public class PaymentBatchRecord {
protected String type;
}
#Getter
#Setter
#JsonPropertyOrder({
// "type",
"transmit_id",
"password",
"creation_date",
"creation_time",
"file_format_code",
"file_reference_code"
})
class FHR extends PaymentBatchRecord implements Serializable {
private final static long serialVersionUID = -584359005702082280L;
// #JsonProperty("type")
// private String type;
#JsonProperty("transmit_id")
private String transmitId;
#JsonProperty("password")
private String password;
#JsonProperty("creation_date")
private String creationDate;
#JsonProperty("creation_time")
private String creationTime;
#JsonProperty("file_format_code")
private String fileFormatCode;
#JsonProperty("file_reference_code")
private String fileReferenceCode;
}
#Setter
#Getter
#JsonPropertyOrder({
// "type",
"transaction_type",
"merchant_id",
"merchant_name",
"batch_entry_description",
"batch_reference_code",
"batch_number"
})
class BHR extends PaymentBatchRecord implements Serializable {
private final static long serialVersionUID = 1650905882208990490L;
// #JsonProperty("type")
// private String type;
#JsonProperty("transaction_type")
private String transactionType;
#JsonProperty("merchant_id")
private String merchantId;
#JsonProperty("merchant_name")
private String merchantName;
#JsonProperty("batch_entry_description")
private String batchEntryDescription;
#JsonProperty("batch_reference_code")
private String batchReferenceCode;
#JsonProperty("batch_number")
private Integer batchNumber;
}
and here is I'm trying to de-serialize
CsvMapper mapper = new CsvMapper();
// uncomment it to run but with all the null values
// mapper.enable(CsvParser.Feature.IGNORE_TRAILING_UNMAPPABLE)
;
CsvSchema sclema = mapper.schemaFor(PaymentBatchRecord.class)
.withoutHeader();
MappingIterator<PaymentBatchRecord> iterator = mapper
.readerFor(PaymentBatchRecord.class)
.with(sclema)
.readValues(in);
List<PaymentBatchRecord> ppojos = iterator.readAll();
and here is the sample csv input
FHR,BILLER_1,"biller1pwd","20200224","091503","CSV","202002240915031"
BHR,"PMT","BILLER_1","BILLER 1 NAME","UTILITY BILL",,1
Exception I got:
Exception in thread "main" com.fasterxml.jackson.dataformat.csv.CsvMappingException: Too many entries: expected at most 1 (value #1 (8 chars) "BILLER_1")
at [Source: (com.fasterxml.jackson.dataformat.csv.impl.UTF8Reader); line: 1, column: 5]
at com.fasterxml.jackson.dataformat.csv.CsvMappingException.from(CsvMappingException.java:28)
at com.fasterxml.jackson.dataformat.csv.CsvParser._reportCsvMappingError(CsvParser.java:1246)
at com.fasterxml.jackson.dataformat.csv.CsvParser._handleExtraColumn(CsvParser.java:1001)
at com.fasterxml.jackson.dataformat.csv.CsvParser._handleNextEntry(CsvParser.java:862)
at com.fasterxml.jackson.dataformat.csv.CsvParser.nextToken(CsvParser.java:609)
at com.fasterxml.jackson.core.util.JsonParserSequence.switchAndReturnNext(JsonParserSequence.java:234)
at com.fasterxml.jackson.core.util.JsonParserSequence.nextToken(JsonParserSequence.java:152)
at com.fasterxml.jackson.core.JsonParser.nextFieldName(JsonParser.java:861)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:295)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:189)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:130)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1196)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
at com.fasterxml.jackson.databind.MappingIterator.nextValue(MappingIterator.java:280)
at com.fasterxml.jackson.databind.MappingIterator.readAll(MappingIterator.java:320)
at com.fasterxml.jackson.databind.MappingIterator.readAll(MappingIterator.java:306)

Jackson Xml: How to add namespace only on root?

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

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?

XML parsing returns 40 counts of IllegalAnnotationExceptions

I am trying to parse the XML response to an object but it throws exception.
The link of response is this:
<response>
<meta>
<per_page>10</per_page>
<total>20</total>
<geolocation>None</geolocation>
<took>8</took>
<page>1</page>
</meta>
<events>
<event>
...
</event>
<event>
...
</event>
....
</events>
</response>
Code
queryString = queryString.replaceAll(" ", "%20");
try {
URL page = new URL(queryString);
HttpURLConnection conn = (HttpURLConnection) page.openConnection();
conn.connect();
InputStreamReader in = new InputStreamReader(conn.getInputStream(),Charset.forName("UTF-8"));
this.response = (Response) JAXB.unmarshal(in, Response.class);
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
Exception
javax.xml.bind.DataBindingException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 40
counts of IllegalAnnotationExceptions
Class has two properties of the same name "events"
this problem is related to the following location:
....
Object classes
#XmlRootElement(name = "Response")
public class Response {
#XmlElement(name="per_page")
private int per_page;
#XmlElement(name="total")
private int total;
#XmlElement(name="geolocation")
private String geolocation;
#XmlElement(name="took")
private int took;
#XmlElement(name="page")
private int page;
#XmlElement(name="events")
private List<Event> events = null;
**getters and setters**
Objects
#XmlRootElement(name="event")
public class Event {
#XmlElement(name = "links")
private String link;
#XmlElement(name = "id")
private int id;
#XmlElement(name = "stats")
private Stats stats;
#XmlElement(name = "title")
private String title;
#XmlElement(name = "announce_date")
private String announce_date;
#XmlElement(name = "score")
private float score;
#XmlElement(name = "date_tbd")
private boolean date_tbd;
#XmlElement(name = "type")
private String type;
#XmlElement(name = "datetime_local")
private String datetime_local;
#XmlElement(name = "visible_until_utc")
private String visible_util_utc;
#XmlElement(name = "time_tbd")
private boolean time_tbd;
#XmlElement(name = "taxonomies")
private List<Taxonomie> taxonomies;
#XmlElement(name = "performers")
private List<Performer> performers;
#XmlElement(name = "url")
private String url;
#XmlElement(name = "created_at")
private String created_at;
#XmlElement(name = "venue")
private Venue venue;
#XmlElement(name = "short_title")
private String short_title;
#XmlElement(name = "datetime_utc")
private String datetime_utc;
#XmlElement(name = "datetime_tbd")
private boolean datetime_tbd;
**getters and setters**
By default JAXB implementations treat public fields and properties as mapped. When you annotate a non-public field it also becomes mapped. Then if you have a mapped field an property with the same name you will get this exception.
When you annotate fields you need to annotate your class with #XmlAccessorType(XmlAccessType.FIELD).
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
Note:
You are currently adding more annotations on your model than you need to. Since JAXB is configuration by exception you only need to add annotations where you want the XML representation to differ from the default.
http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html

Categories

Resources