<?xml version="1.0" encoding="UTF-8"?>
<STATUS_UPDATE>
<UPDATES_COUNTER>2</UPDATES_COUNTER>
<LETTERS>
<LETTER>
<LETTER_KEY>key1</LETTER_KEY>
<STATUS>status1</STATUS>
</LETTER>
<LETTER>
<LETTER_KEY>key2</LETTER_KEY>
<STATUS>status2</STATUS>
</LETTER>
</LETTERS>
</STATUS_UPDATE>
So i have this xml. Im trying to create a pojo from it.
#XmlRootElement(name = "STATUS_UPDATE")
public class StatusUpdate {
private int updatesCounter;
List<Letter> letters;
public StatusUpdate(){
letters = new ArrayList<Letter>();
}
public StatusUpdate(int updatesCounter, List<Letter> letters){
super();
this.updatesCounter = updatesCounter;
this.letters = letters;
}
#XmlElement(name="LETTERS")
public List<Letter> getLetters() {
return letters;
}
public void setLetters(List<Letter> letters) {
this.letters = letters;
}
#XmlElement(name="UPDATES_COUNTER")
public int getUpdatesCounter() {
return updatesCounter;
}
public void setUpdatesCounter(int updatesCounter) {
this.updatesCounter = updatesCounter;
}
}
And letter class
#XmlRootElement(name = "LETTER")
public class Letter {
public Letter(){
}
public Letter(String letterKey,String status){
this.letterKey = letterKey;
this.status = status;
}
String letterKey;
String status;
#XmlElement(name="LETTER_KEY")
public String getLetterKey() {
return letterKey;
}
public void setLetterKey(String letterKey) {
this.letterKey = letterKey;
}
#XmlElement(name="STATUS")
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
and this code snipper wont seem to do the job.
i get no errors, code runs, i get a 2 in my updateCounter variable,
but my list is empty with only one letter object, with both variables null.
File file = new File("myFile.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(StatusUpdate.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StatusUpdate msgObject = (StatusUpdate) jaxbUnmarshaller.unmarshal(file);
I've been reading the documentations and examples, but most of them are either written to match variable names and xml elemenets (which i dont want to) or are either not helpfull to me.
Any ideas? I have a feeling i'm very close to my goal but i can't seem to grasp what i'm missing.
You need to add #XmlElementWrapper(name = "LETTERS") annotation to getLetters() method like this
#XmlElementWrapper(name = "LETTERS")
#XmlElement(name = "LETTER")
public List<Letter> getLetters() {
return letters;
}
Related
I'm using XMLMapper to deserialize AIML code (mostly the same as XML) and got this problem when I mix text and tags under the same tag. Example:
<set name="setVar">
<srai>FUNCTION1</srai>
<srai>FUNCTION2<star index="1"/></srai>
</set>
My java classes definition are:
#JacksonXmlRootElement(localName = "set")
public class Set {
#JacksonXmlProperty(isAttribute = true, localName = "name")
public String name;
#JacksonXmlElementWrapper(useWrapping = false)
#JacksonXmlProperty(localName = "srai")
public List<Srai> srais;
public Set() {
}
public Set(String name, List<Srai> srais) {
this.name = name;
this.srais = srais;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Srai> getSrais() {
return srais;
}
public void setSrais(List<Srai> srais) {
this.srais = srais;
}
}
#JacksonXmlRootElement(localName = "srai")
public class Srai {
#JacksonXmlElementWrapper(useWrapping = false)
#JacksonXmlProperty(localName = "star")
public List<Star> stars;
#JacksonXmlText
public String text;
public Srai() {}
public Srai(String text, List<Star> stars) {
this.text = text;
this.stars = stars;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public List<Star> getStars() {
return stars;
}
public void setStars(List<Star> stars) {
this.stars = stars;
}
}
#JacksonXmlRootElement(localName = "star")
public class Star {
#JacksonXmlProperty(isAttribute = true)
public int index;
public Star() {
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
If I try to deserialize code in which there are only text or only stars into the srai, everything works perfect, the problem appears when I mix text and tags.
I finally managed to solve it upgrading my jackson-databind, jackson-core, jackson-annotation and jackson-dataformat-xml to the version 2.12.1 as tnci suggested here.
To do this, just change their version into the pom.xml to 2.12.1 or later versions.
Then I created a new property into the star class:
#JacksonXmlText
public String text;
This way, when there is text before or between xml tags, it will be saved in this property on a star object. If the text is after all xml tags, there will appear this problem:
Unexpected end-of-input when trying read value of type `**root-agent-class**`
So the problem persists but now it's much better since we can read some text+tags code.
i have a xml and i want to save into a string the sub xml formed by the child of a specific tag.
this is a xml example:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SampleDTO>
<id>1</id>
<someList>
<someObject>
<amount>32</amount>
<id>1</id>
<someDescription>I am a description</someDescription>
</someObject>
<someObject>
<amount>66</amount>
<id>2</id>
<someDescription>I am another description</someDescription>
</someObject>
<someObject>
<amount>78</amount>
<id>13</id>
<someDescription>Guess what? I am a description</someDescription>
</someObject>
</someList>
<otherList>
<otherObject>
<flag>true</flag>
<id>1</id>
<otherDescription>Oh nice, a description</otherDescription>
</otherObject>
</otherList>
</SampleDTO>
i want , passing for example "someList" , to save into a String the sub-xml element and value, because next i deserialize it into a java object
use the JAXB unmarshaller for converting xml document into java objects.
firstly add JAXB dependency into your project's classpath. for more info
SampleDTO.java
#XmlRootElement
public class SampleDTO {
private String id;
private List<SomeList> someList;
private List<OtherList> otherList;
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement
public List<SomeList> getSomeList() {
return someList;
}
public void setSomeList(List<SomeList> someList) {
this.someList = someList;
}
#XmlElement
public List<OtherList> getOtherList() {
return otherList;
}
public void setOtherList(List<OtherList> otherList) {
this.otherList = otherList;
}
}
SomeList.java
#XmlRootElement
public class SomeList {
private List<SomeObject> someObject;
#XmlElement
public List<SomeObject> getSomeObject() {
return someObject;
}
public void setSomeObject(List<SomeObject> someObject) {
this.someObject = someObject;
}
}
OtherList.java
#XmlRootElement
public class OtherList {
private List<OtherObject> otherObject;
#XmlElement
public List<OtherObject> getOtherObject() {
return otherObject;
}
public void setOtherObject(List<OtherObject> otherObject) {
this.otherObject = otherObject;
}
}
SomeObject.java
#XmlRootElement
public class SomeObject {
private String amount;
private String id;
private String someDescription;
#XmlElement
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement
public String getSomeDescription() {
return someDescription;
}
public void setSomeDescription(String someDescription) {
this.someDescription = someDescription;
}
}
OtherObject.java
#XmlRootElement
public class OtherObject {
private String flag;
private String id;
private String otherDescription;
#XmlElement
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement
public String getOtherDescription() {
return otherDescription;
}
public void setOtherDescription(String otherDescription) {
this.otherDescription = otherDescription;
}
}
Unmarshalling with JAXB
public class Main {
public static void main(String[] args) {
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(SampleDTO.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
SampleDTO sampleDTO= (SampleDTO) jaxbUnmarshaller.unmarshal(file);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Your java class/object should have at least these 3 instance vars :
private int amount
private int id
private String description
Then use some xml parsing library (eg jdom2), and for each <someObject> tag you iterate through, initialize a new object of your class and assign to it the values parsed from the xml (amount / id / description) , and add each newly created object in a List or array etc..
There are many open-source XML processing packages available.
I like Jackson.
Here is a link to a Baeldung Article about Jackson XML
The summary is this:
Add a Jackson dependency to your POM.
Create an object structure that represents your xml structure.
Create an XmlMapper.
Use the XmlMapper.
I have an xml input as below
<Confirmations>
<SystemConf>
<SysCnf>
<MessageId>1</MessageId>
</SysCnf>
<SysCnf>
<MessageId>2</MessageId>
</SysCnf>
</SystemConf>
</Confirmations>
and these are my classes
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Confirmations")
public class Confirmations
{
#XmlElementWrapper(name = "SystemConf")
#XmlElement(name = "SysCnf")
private List<SystemConfirmation> systemConfirmations = null;
public List<SystemConfirmation> getSystemConfirmations()
{
return systemConfirmations;
}
public void setSystemConfirmations(List<SystemConfirmation> systemConfirmations)
{
this.systemConfirmations = systemConfirmations;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "SysCnf")
public class SystemConfirmation
{
#XmlElement(name = "MessageId")
private String messageId;
public void setMessageId(String messageId)
{
this.messageId = messageId;
}
public String getMessageId()
{
return messageId;
}
#XmlAnyElement(value = SysConfXmlStringHandler.class)
private String xml;
public String getXml()
{
return xml;
}
}
public class SysConfXmlStringHandler implements DomHandler<String, StreamResult>
{
private static final String SYSCONF_START_TAG = "<SycCnf>";
private static final String SYSCONF_END_TAG = "</SysCnf>";
private StringWriter xmlWriter = new StringWriter();
#Override
public StreamResult createUnmarshaller(ValidationEventHandler errorHandler)
{
return new StreamResult(xmlWriter);
}
#Override
public String getElement(StreamResult rt)
{
String xml = rt.getWriter().toString();
System.out.println(xml);
int beginIndex = xml.indexOf(SYSCONF_START_TAG) + SYSCONF_START_TAG.length();
int endIndex = xml.indexOf(SYSCONF_END_TAG);
return xml.substring(beginIndex, endIndex);
}
#Override
public Source marshal(String n, ValidationEventHandler errorHandler)
{
try
{
String xml = SYSCONF_START_TAG + n.trim() + SYSCONF_END_TAG;
StringReader xmlReader = new StringReader(xml);
return new StreamSource(xmlReader);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
Question
In the xml field of the SystemConfirmation class I want the whole xml of that particular SystemConfirmation block xmml element, which would be below
<SysCnf>
<MessageId>1</MessageId>
</SysCnf>
Does anyone know how to achieve this? I tried the above code but I was only getting MessageId block in the xml and if I add multiple fields in SystemConfirmation than I only get the first one.
How to serialize this class using Jackson
package com.progressivebeef.service.response;
#XmlRootElement(name = "response")
#XmlSeeAlso({ User.class, Profile.class,MenuItem.class,Feedlot.class,Document.class,FeedlotDocument.class })
public final class PBResponse {
private Integer status = FAILURE;
private String code;
private String message;
private Integer totalRecords;
private List<Model> list = new ArrayList<Model>();
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
#XmlElementWrapper(name = "PBBeans")
#XmlAnyElement
public List<Model> getList() {
return list;
}
public void setList(List<Model> list) {
this.list = list;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Integer getTotalRecords() {
return totalRecords;
}
public void setTotalRecords(Integer totalRecords) {
this.totalRecords = totalRecords;
}
/**
* #author tiqbal
* Resets the response.
*/
public void reset(){
this.status = FAILURE;
this.list = new ArrayList<Model>();
this.code = null;
this.message = null;
this.totalRecords = null;
}
}
Jackson is not picking up #XmlElementWrapper #XmlSeeAlso annotations, also Jackson is not mapping #XmlRootElement annotation. I am using Jackson 1.9.0. Jackson is putting elements in the list but not mapping root element of POJO classes.
Here is sample method.
package com.progressivebeef.service.impl;
#Service("/ActivityServiceImpl/")
#Path("/activityservice/")
public class ActivityServiceImpl implements ActivityService {
#POST
#Produces(MediaType.APPLICATION_JSON)
#Override
public Response inputJson(User user ) {
System.out.println("user ");
user.setUser_name("check user name");
Profile profile = new Profile();
profile.setFirst_name("abc");
profile.setLast_name("khan");
user.setProfile( profile );
PBResponse response = new PBResponse();
response.getList().add(user);
return Response.ok(response).build();
}
}
The response it generating is '{"response":{"status":0,"PBBeans":[{"user_name":"check user name","password":"click123","user_role_key":2,"profile":{"first_name":"abc","last_name":"khan","tableName":"pb_profile","pk":"profile_id"},"tableName":"pb_user","pk":"user_id"}]}}'
not picking up the bean's root name inside PBBeans tag.
Hope this helps. Basically, you need to set the WRAP_ROOT_VALUE to true in your mapper.
In a personal project i have the following xml structures to make my own java classes:
first:
<response>
<action>getcredits</action>
<data>
<account>
<username>aptalaser</username>
<balance>193</balance>
</account>
</data>
second:
<response>
<action>getMsgInfoByID</action>
<data>
<messageid>c36d7ee5-16f9-4193-9a75-0537e590e9d3</messageid>
<originator>+17036231081 [4]</originator>
<recipient>10958</recipient>
<folder>INBOX</folder>
<senttime>2011.10.17 13:10:26</senttime>
<receivedtime>2011.10.17 13:10:26</receivedtime>
<creationtime>2011.10.17 13:10:26</creationtime>
<callbackid/>
<state>0</state>
<operatornames>
<operatorname>SMPP0</operatorname>
</operatornames>
<routes>
<route>defin_admin</route>
</routes>
<optionalfields>
<optionalfield name="30" value="35333131572D31303133322D303530364E2D333434544600"/>
<optionalfield name="8528" value="017F"/>
</optionalfields>
<messagetype>SMS:TEXT</messagetype>
<messagedata>Test message</messagedata>
</data>
third:
<response>
<action>sendmessage</action>
<data>
<acceptreport>
<statuscode>0</statuscode>
<statusmessage>Message accepted for delivery</statusmessage>
<messageid>0f06bbd9-0894-4fb4-9c4b-68e29363d299</messageid>
<originator>aptalaser</originator>
<recipient>8588430260</recipient>
<messagetype>SMS:TEXT</messagetype>
<messagedata>Bom dia cara.</messagedata>
</acceptreport>
</data>
The structures are divided in two places: a action to indicating the method acessed and the data section: a generic portion with will change the content dinamically in response to access a different method.
Following this idea i decide create a Response class with two field: a String action and a field Data:
The Response class:
/* omite desnecessary imports */
#XmlRootElement(name = "response")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class Response {
private String action;
private Data data;
public String getAction() {
return action;
}
/* omitted set methds */
#XmlElementRef
public Data getData() {
return data;
}
#Override
public String toString() {
String template = "( action: %s, data: %s )";
return String.format(template, this.action, this.data);
}
}
The Data class, the base class for all Data sections
#XmlSeeAlso({ GetInfoMessageData.class, GetAccountData.class, SendMessageData.class })
public class Data {
}
The GetAccountClass to represent account retrieve information
/* omite desnecessary imports */
#XmlRootElement(name = "data")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class GetAccountData extends Data {
private List<Account> account;
public List<Account> getAccount() {
return account;
}
/* omitted set methos */
#Override
public String toString() {
return String.format("Account( %s )", this.account);
}
public static class Account {
private String username;
private Long balance;
public String getUsername() {
return username;
}
public Long getBalance() {
return balance;
}
/* omitted set methods */
#Override
public String toString() {
return String.format("[ usr: %s, credit: %d ]", this.username, this.balance);
}
}
}
The class represents the message data
/* omite desnecessary imports */
#XmlRootElement(name = "data")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class GetInfoMessageData extends Data {
private String messageId;
private String destino;
private String recipiente;
private String folder;
private Date dataCricao;
private Date dataEnvio;
private Date dataRecebimento;
private Integer status;
private String tipoMensagem;
private String mensagem;
private List<Protocolo> protocolos;
private List<Route> rotas;
private List<Field> optionalFields;
private Error error;
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Protocolo {
private String nomeProtocolo;
#XmlElement(name = "operatorname", defaultValue = "")
public String getNomeProtocolo() {
return nomeProtocolo;
}
/* omitted set methods */
}
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Error {
private String errorMessage;
#XmlElement(name = "errormessage")
public String getErrorMessage() {
return errorMessage;
}
/* omitted set methods */
}
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Route {
private String route;
#XmlElement(defaultValue = "")
public String getRoute() {
return route;
}
/* omitted set methods */
}
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class Field {
private String name;
private String value;
#XmlAttribute
public String getName() {
return name;
}
#XmlAttribute
public String getValue() {
return value;
}
/* omitted set methods */
}
#XmlElement(name = "messageid", required = true)
public final String getMessageId() {
return messageId;
}
#XmlElement(name = "originator", required = true)
public final String getDestino() {
return destino;
}
#XmlElement(name = "recipient", defaultValue = "")
public final String getRecipiente() {
return recipiente;
}
#XmlElement(name = "folder", defaultValue = "")
public final String getFolder() {
return folder;
}
#XmlElement(name = "creationtime")
#XmlJavaTypeAdapter(type = Date.class, value = JavaDateAdapter.class)
public final Date getDataCricao() {
return dataCricao;
}
#XmlElement(name = "senttime")
#XmlJavaTypeAdapter(type = Date.class, value = JavaDateAdapter.class)
public final Date getDataEnvio() {
return dataEnvio;
}
#XmlElement(name = "receivedtime")
#XmlJavaTypeAdapter(type = Date.class, value = JavaDateAdapter.class)
public final Date getDataRecebimento() {
return dataRecebimento;
}
#XmlElement(name = "state", required = true)
public final Integer getStatus() {
return status;
}
#XmlElement(name = "messagetype", required = true)
public final String getTipoMensagem() {
return tipoMensagem;
}
#XmlElement(name = "messagedata")
public final String getMensagem() {
return mensagem;
}
#XmlElement(name = "operatornames")
public final List<Protocolo> getProtocolos() {
return protocolos;
}
#XmlElement(name = "routes")
public final List<Route> getRotas() {
return rotas;
}
#XmlElement(name = "optionalfield")
#XmlElementWrapper(name = "optionalfields")
public List<Field> getOptionalFields() {
return optionalFields;
}
#XmlElement(name = "error")
public Error getError() {
return error;
}
/* omitted set methods */
}
The class represent the sendMessage response operation
/* omite desnecessary imports */
#XmlRootElement(name = "data")
#XmlAccessorType(XmlAccessType.PROPERTY)
public class SendMessageData extends Data {
private AcceptReport acceptReport;
#XmlElement(name = "acceptreport")
public AcceptReport getAcceptReport() {
return acceptReport;
}
#SuppressWarnings("unused")
public void setAcceptReport(AcceptReport acceptReport) {
this.acceptReport = acceptReport;
}
#Override
public String toString() {
return String.format("Report( %s )", this.acceptReport);
}
#XmlRootElement(name = "acceptreport")
public static class AcceptReport {
private Integer status;
private String statusMessage;
private String messageId;
private String originator;
private String recipient;
private String messageType;
private String messageData;
#XmlElement(name = "statuscode")
public Integer getStatus() {
return status;
}
#XmlElement(name = "statusmessage")
public String getStatusMessage() {
return statusMessage;
}
#XmlElement(name = "messageid")
public String getMessageId() {
return messageId;
}
#XmlElement(name = "originator")
public String getOriginator() {
return originator;
}
#XmlElement(name = "recipient")
public String getRecipient() {
return recipient;
}
#XmlElement(name = "messagetype")
public String getMessageType() {
return messageType;
}
#XmlElement(name = "messagedata")
public String getMessageData() {
return messageData;
}
/* set methods omited */
#Override
public String toString() {
return String.format("[ stats: %d, msgId: %s, msg: %s ]", this.status, this.messageId, this.messageData);
}
}
}
The xml 'data' section don't have anything to identifing 'what son of Data will be use in the ummarshaling operation?'
My test works fine in the marshalling operation but in the ummarshalling its broken because the jaxb don't identify the son of Data to use.
/* omite desnecessary imports */
public class Teste {
public static void main(String[] args) throws JAXBException {
JAXBContext ctx = JAXBContext.newInstance(
Data.class, GetAccountData.class,
GetInfoMessageData.class, Response.class, SendMessageData.class
);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Response r = new Response();
r.setAction("getcredits");
GetAccountData data = new GetAccountData();
Account account = new GetAccountData.Account();
account.setUsername("aptalaser");
account.setBalance(12523L);
data.setAccount(Arrays.asList(account));
r.setData(data);
//in there is equal to first xml
marshaller.marshal(r, System.out);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
Response resp = (Response) unmarshaller.unmarshal(new File("./get-credits.xml"));
//but in there the resp retrieved is to the third xml(and the Data field is null)
System.out.println(resp);
}
}
The question is: I need implement my own customized converter or i can make that with simple configurations like annotations?
Thanks for help.
For the purposes of unmarshalling you need to have a unique identifier that can be used to identify the appropriate subclass to instantiate.
#XmlElementRef
Since you are using #XmlElementRef this unique identifier should be the #XmlRootElement of the subclass, but you currently have all subclasses with #XmlRootElement(name="data").
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
xsi:type Attribute
If you want to always leverage the data element then you need something else as the inheritance indicator. If you remove the #XmlElementRef annotation then JAXB will leverage the xsi:type attribute for this purpose.
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
Element Content
If neither of the above will work for you then you can leverage an XmlAdapter.
http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html
UPDATE
Thank you #Blaise this help me so much, one more thing: i need process
this model, so i need implement different response class with
appropriate subclass field?
If you know which type of response you are receiving this is a valid approach. Then when you do the unmarshal you can specify the class you are unmarshalling. You will need to do this because the response root element corresponds to all the response classes you will make.
GetAccountDataResponse = unmarshaller.unmarshal(xml, GetAccountDataResponse.class).getValue();
If you don't know what type of response you are receiving and need to decide based on the content of the message then an XmlAdapter approach for handling inheritance will work.
http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html