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;
Related
I have this soap response below and I need to parse it to a java object ...But all the fields are returning null when I print it. Below the classes that I am using to interpret the response.
I have been trying to use jaxb to unmarshall the content but none of my tries went right.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:consultarPosicaoCotistaOnOfflineFundoResponse>
<ns2:listCotistaFundo>
<ns2:cotistaFundo>
<ns2:cdFundo>014588</ns2:cdFundo>
<ns2:nmCliente>BRADESCO FIC FIM GAVEA MACRO</ns2:nmCliente>
<ns2:dtPosicao>2021-02-18</ns2:dtPosicao>
<ns2:vlCota>1.3121393</ns2:vlCota>
<ns2:qtdCotas>43650.7211055</ns2:qtdCotas>
<ns2:vlCorrigido>57275.83</ns2:vlCorrigido>
<ns2:vlIr>221.1</ns2:vlIr>
<ns2:vlIof>0</ns2:vlIof>
<ns2:vlLiquidoResg>57054.73</ns2:vlLiquidoResg>
<ns2:vlContaCorrente>57054.73</ns2:vlContaCorrente>
<ns2:vlContaInvestimento>57054.73</ns2:vlContaInvestimento>
<ns2:noCnpjFundo>28428211000149</ns2:noCnpjFundo>
<ns2:icAfPosicao>F</ns2:icAfPosicao>
<ns2:vlAplicacao>49069.11</ns2:vlAplicacao>
<ns2:qtCotasBloqueadas>0</ns2:qtCotasBloqueadas>
<ns2:vlBrutoBloqueado>0</ns2:vlBrutoBloqueado>
<ns2:vlLiquidoBloqueado>0</ns2:vlLiquidoBloqueado>
<ns2:vlAplicacaoNaoCotizada>0</ns2:vlAplicacaoNaoCotizada>
<ns2:vlResgateNaoCotizado>0</ns2:vlResgateNaoCotizado>
</ns2:cotistaFundo>
</ns2:listCotistaFundo>
<ns3:responseMessages>
<ns3:message>
<ns3:code>EJCOT-0000</ns3:code>
<ns3:desc>Operação Concluída</ns3:desc>
</ns3:message>
</ns3:responseMessages>
</ns2:consultarPosicaoCotistaOnOfflineFundoResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Unmarshaller unmarshaller = JAXBContext.newInstance(ConsultarPosicaoCotistaOnOfflineFundoResponse.class).createUnmarshaller();
response = (ConsultarPosicaoCotistaOnOfflineFundoResponse) unmarshaller.unmarshal(soapMessage.getSOAPBody().extractContentAsDocument());
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "consultarPosicaoCotistaOnOfflineFundoResponse",
namespace = "http://totvs.cot.webservices")
public class ConsultarPosicaoCotistaOnOfflineFundoResponse implements Serializable {
private ListCotistaFundo[] listCotistaFundo;
public ListCotistaFundo[] getCotistaFundo() {
return listCotistaFundo;
}
#XmlElement(name ="listCotistaFundo",
namespace = "http://totvs.cot.webservices")
public void setCotistaFundos(ListCotistaFundo[] listCotistaFundo) {
this.listCotistaFundo = listCotistaFundo;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
public class ListCotistaFundo {
#XmlElement(name = "cdFundo")
private String cdFundo;
#XmlElement(name = "nmCliente")
private String nmCliente;
#XmlElement(name = "dtPosicao")
private Date dtPosicao;
#XmlElement(name = "vlCota")
private BigDecimal vlCota;
#XmlElement(name = "qtdCotas")
private BigDecimal qtdCotas;
#XmlElement(name = "vlCorrigido")
private BigDecimal vlCorrigido;
#XmlElement(name = "vlIr")
private BigDecimal vlIr;
#XmlElement(name = "vlIof")
private BigDecimal vlIof;
#XmlElement(name = "vlLiquidoResg")
private BigDecimal vlLiquidoResg;
#XmlElement(name = "vlContaCorrente")
private BigDecimal vlContaCorrente;
#XmlElement(name = "vlContaInvestimento")
private BigDecimal vlContaInvestimento;
#XmlElement(name = "noCnpjFundo")
private double noCnpjFundo;
#XmlElement(name = "icAfPosicao")
private String icAfPosicao;
#XmlElement(name = "vlAplicacao")
private BigDecimal vlAplicacao;
#XmlElement(name = "qtCotasBloqueadas")
private BigDecimal qtCotasBloqueadas;
#XmlElement(name = "vlBrutoBloqueado")
private BigDecimal vlBrutoBloqueado;
#XmlElement(name = "vlLiquidoBloqueado")
private BigDecimal vlLiquidoBloqueado;
#XmlElement(name = "vlAplicacaoNaoCotizada")
private BigDecimal vlAplicacaoNaoCotizada;
#XmlElement(name = "vlResgateNaoCotizado")
private BigDecimal vlResgateNaoCotizado;
Getters and Setters ...
Does anyone have a tip in how to convert the response in Java object?
It could be an issue with namespace since I dont see namespaces declared in the XML. You could try setting the namespace as empty in both #XmlRootElement and #XmlElement as follows :
#XmlRootElement(name = "consultarPosicaoCotistaOnOfflineFundoResponse",
namespace = "")
#XmlElement(name ="listCotistaFundo", namespace = "")
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 {
}
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.
I'm getting null value when unmarshelling the xml file to java class. But the xml file as values corresponding to the its attributes. Is there any mistake in my pojo class or unmarshelling?
Please help
This is my pojo
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "CardUpdateResponse",namespace="http://www.samople.com/Prepaid")
public class FVCardUpdateResponse {
#XmlElement(name = "AccountNumber")
private String AccountNumber;
#XmlElement(name = "ResCode")
private String ResCode;
#XmlElement(name = "ResErrorCode")
private String ResErrorCode;
#XmlElement(name = "ResErrorMsg")
private String ResErrorMsg;
//Setters and Getters
}
This is my xml file
<?xml version="1.0" encoding="UTF-8"?>
<CardUpdateResponse xmlns="http://www.samople.com/Prepaid">
<CARDUPDATE_RET>
<ResErrorMsg>ID Issue Date must be equal or less than present date</ResErrorMsg>
<ResErrorCode>ErrIsud01</ResErrorCode>
<ResCode>0</ResCode>
<ACCOUNTNUMBER>2000000003918246</ACCOUNTNUMBER>
</CARDUPDATE_RET>
</CardUpdateResponse>
this is code for unmarshelling
public class XmlUnmarshelling {
public void unmarshell()
{
try
{
System.out.println("xml unmarshelling class");
File file = new File("D:/var/lib/tomcat7/webapps/tmpFiles/1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(FVCardUpdateResponse.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
FVCardUpdateResponse CARDUPDATE_ret = (FVCardUpdateResponse) jaxbUnmarshaller.unmarshal(file);
System.out.println("xml unmarshelled = "+CARDUPDATE_ret.getResErrorMsg());//Getting null value as response.
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Your POJO doesn't have the same structure as your XML. CardUpdateResponse doesn't directly contain the properties in your POJO, it contains CARDUPDATE_RET element which contains the properties.
You could modify your POJO like this to match the XML:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "CardUpdateResponse", namespace="http://www.samople.com/Prepaid")
public static class CardUpdateResponseWrapper {
#XmlElement(name="CARDUPDATE_RET")
private FVCardUpdateResponse response;
// Getter and setter for response
public static class FVCardUpdateResponse {
#XmlElement(name = "AccountNumber")
private String AccountNumber;
#XmlElement(name = "ResCode")
private String ResCode;
#XmlElement(name = "ResErrorCode")
private String ResErrorCode;
#XmlElement(name = "ResErrorMsg")
private String ResErrorMsg;
// Getters and setters
}
}
Now the CardUpdateResponseWrapper class will represent your root XML element and it will have instance of FVCardUpdateResponse which will represent the CARDUPDATE_RET XML element.
Do unmarshall it, just call:
File file = new File("D:/var/lib/tomcat7/webapps/tmpFiles/1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(CardUpdateResponseWrapper.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
CardUpdateResponseWrapper wrapper = (CardUpdateResponseWrapper) jaxbUnmarshaller.unmarshal(file);
System.out.println(wrapper.getResponse().getResErrorMsg());
I think that the issue is a combination of two problems, one what Bohuslav is saying, the other you need to repeat your namespace on every XmlElement annotation, e.g.
#XmlElement(name = "ResCode", namespace="http://www.samople.com/Prepaid")
and one particular issue, you need to match the cases as well so the name for AccountNumber should be capitalized ACCOUNTNUMBER
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