I want to know if it's possible to ignore one or many nodes when parsing XML using Jackson ML module.
I want to be able to parse this XML
<bundle>
<id value="myBundleId"/>
<meta>
<profile value="http://myurl/profile1" />
<profile value="http://myurl/profile2" />
<tag>
<system value="https://myurl/system" />
<code value="myAppCode"/>
</tag>
</meta>
<type value="message" />
</bundle>
into this POJO object
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import lombok.Data;
#Data
public class Bundle {
#JacksonXmlElementWrapper(localName = "id")
#JacksonXmlProperty(isAttribute = true, localName = "value")
private String id;
#JacksonXmlElementWrapper(localName = "type")
#JacksonXmlProperty(isAttribute = true, localName = "value")
private String type;
}
Right now it's not working as I think the annotation #JacksonXmlElementWrapper is only working with lists.
It also gives me the following error message :
java.lang.IllegalArgumentException: Conflicting setter definitions for
property "value"
Try the following:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Bundle {
...
}
Alternatively:
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
If I recall right you can set this on the object mapper and it will avoid exceptions being thrown on unmatched nodes.
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
to bind properties you can use #JsonProperty
Jackson Annotations
If you don't mind using a different library, SimpleXml does this by default:
public class Bundle {
#XmlWrapperTag("id")
#XmlName("value")
#XmlAttribute
private String id;
#XmlWrapperTag("type")
#XmlName("value")
#XmlAttribute
private String type;
}
And then serialize and print:
final SimpleXml simple = new SimpleXml();
final Bundle bundle = simple.fromXml(xml, Bundle.class);
System.out.println(bundle.id + " : " + bundle.type);
This will print:
myBundleId : message
SimpleXml is in maven central
<dependency>
<groupId>com.github.codemonstur</groupId>
<artifactId>simplexml</artifactId>
<version>1.5.5</version>
</dependency>
Related
I am trying to call an API in my Dropwizard application, the API returns the following class
#Getter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#EqualsAndHashCode
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Error")
public class ErrorResponse {
#NonNull
#XmlAttribute(name = "msg")
private String msg;
#XmlAttribute(name = "display")
protected String display;
#XmlAttribute(name = "action")
private String action;
#NotNull
#XmlAttribute(name = "type")
private String type;
#XmlAttribute(name = "code")
private String code;
}
return javax.ws.rs.core.Response.status(status).entity(ErrorResponse).header(CONTENT_TYPE, APPLICATION_XML).build();
when I ran the application from IntelliJ, I got this response:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Error msg="some message" display="true" action="RETRY" type="FATAL" code="error"/>
when I ran it using the jar, I got this:
<ErrorResponse>
<msg>some message</msg>
<display>true</display>
<action>RETRY</action>
<type>FATAL</type>
<code>error</code>
</ErrorResponse>
I am using dropwizard 2.0.24
Don't package your jar plain, use the maven-shade-plugin, that will include the dependencies in the jar, or, add these dependencies to your Classpath.
after deleting .m2 I got same response from IntelliJ and from the Jar.
for some reason Dropwizard 2.0.24 was ignoring my JAXB annotations, I fixed it by changing the version to 2.0.0.
thanks for your comments, much appreciated
when I try to parse the following XML I get an error with Jackson:
<root>
<aCollection>
<language xml:lang="en">
<entry id="1" value="foo"/>
<entry id="2" value="bar"/>
<entry id="3" value="blah"/>
</language>
</aCollection>
</root>
The classes I use are:
#Data
#JacksonXmlRootElement(localName = "root")
public class RootDTO {
#JacksonXmlProperty(localName = "aCollection")
private CollectionDTO collection;
}
#Data
public class CollectionDTO {
#JacksonXmlProperty(localName = "language")
LanguageDTO language;
}
#Data
public class LanguageDTO {
#JacksonXmlProperty(localName = "entry")
EntryDTO[] entries;
}
#Data
public class EntryDTO {
#JacksonXmlProperty(isAttribute = true)
private String id;
#JacksonXmlProperty(isAttribute = true)
private String value;
}
The error is:
Can not instantiate value of type [simple type, class com.tinkerdesk.viewer.model.DTO.EntryDTO] from JSON String; no single-String constructor/factory method
If I remove the array brackets it works but of course only parses the first entry element. I found that I should maybe use #JacksonXmlElementWrapper(useWrapping = false) on top of EntryDTO[] entries; since the elements are not wrapped. However this does not comile for me because there is no useWrapping in the com.fasterxml.jackson.xml.annotate.JacksonXmlElementWrapper. My Jackson core version is 2.9.6 and xml-databind 0.6.2. Is there now a newer way to do this?
Ok nevermind I made a stupid error. The 0.6.2 version of Jackson Dataformat Xml should have made me suspicious in the first place. It seems at some point they moved to a new namespace and I was using the old namespace in gradle which sadly resolved to a maven repository that has last been updated in 2011.
Updating the namespace made it resolve 2.9.5 which has the useWrapping property. Setting this resolved the issue.
I'm currently having a issue in my Project that involes an XML stored in a String variable. I'm trying to save the content of the XML in different variables.
Using the common JAXB method that involes the same variable name and the tag name should solve my problem. In this case, I have to store in different variable names.
This is my XML
<?xml version="1.0" encoding="UTF-8"?>
<PORTAL>
<NAME>PERSON 2.0</NAME>
<ID>99995</ID>
<DATAGENERATIO>2008-04-10</DATAGENERATIO>
<HOURGENERATIO>05:07:35</HOURGENERATIO>
<LANGUAGE>EN</LANGUAGE>
<XMLVERSIO>1.0</XMLVERSIO>
</PORTAL>
And this is the class used to store the content of that XML.
#XmlRootElement(name="PORTAL")
public class ApiPubPortal {
private long idPortal;
private String idIdioma;
private String nombre;
private Date dataGeneracio;
private String versionXml;
}
I already try using #XmlElement(name="ID") but it gives me IllegalAnnotationExceptions
If anybody can help me with this I'll be thankfully.
You need to make sure that you have getter and setters for all your private fields. An example given below:
private long idPortal;
#XmlElement(name = "ID")
public long getIdPortal()
{
return idPortal;
}
public void setIdPortal(long idPortal)
{
this.idPortal = idPortal;
}
Please note to put #XmlElement(name = "ID") on your getter.
With SimpleXml you can use the #XmlName annotation to tell the parser what the XML tag name is that is linked to a field. There is no need for getters and setters in your POJO. This will work:
#XmlName("PORTAL")
public class ApiPubPortal {
#XmlName("ID")
private long idPortal;
private String idIdioma; // Its not clear to which XML tag this maps
#XmlName("NAME")
private String nombre;
#XmlName("DATAGENERATIO")
private String dataGeneracio;
#XmlName("XMLVERSIO")
private String versionXml;
}
Your XML can then be serialized this way:
final String xml = ...
final SimpleXml simple = new SimpleXml();
final ApiPubPortal portal = simple.fromXml(xml, ApiPubPortal.class);
The library is in maven central:
<dependency>
<groupId>com.github.codemonstur</groupId>
<artifactId>simplexml</artifactId>
<version>1.4.0</version>
</dependency>
I am trying to serialize and deserialize XML in the following format:
<home>
<handle>300023</handle>
<command>login</command>
<content>
<user_id>300023</user_id>
<result>0</result>
</content>
</home>
and try to map it to the following classes:
#Data
#JacksonXmlRootElement(localName = "home")
#JsonPropertyOrder({"handle", "command", "content"})
public class Home {
private long handle;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
visible = true,
property = "command")
#JsonSubTypes({
#JsonSubTypes.Type(value = LoginContent.class, name = "login")
})
private BaseContent content;
}
and
#Data
#JsonPropertyOrder({
"user_id",
"result"})
public class LoginContent implements BaseContent{
#JacksonXmlProperty(localName = "user_id")
private long userId;
private int result;
}
The problem occurs when I serialize with
Home home = new Home();
home.setHandle(300023);
LoginContent content = new LoginContent();
content.setUserId(300023);
content.setResult(0);
home.setContent(content);
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.findAndRegisterModules();
String xmlString = xmlMapper.writeValueAsString(home);
xmlString return as xml with correct fields but incorrect order as #JsonPropertyOrder
<home>
<handle>300023</handle>
<content>
<user_id>300023</user_id>
<result>0</result>
</content>
<command>login</command>
</home>
The implementing class type of content is based on the value of <command>, which is in the same level as <content>, hence the usage of JsonTypeInfo.As.EXTERNAL_PROPERTY.
Since the deserialization is working as expected, I excluded other implementing classes of BaseContent.
Now I am looking for a way I can make #JsonPropertyOrder effective with fields that is generated by #JsonTypeInfo. I have tried to add command field in Home.class, but it will result in a duplicated tag in the xml.
What would be the correct way to achieve this? Or is there other better way of doing this (serialize and deserialize mentioned xml with content varies based on command value with specific order)?
Thanks in advance.
I am getting the following error when trying to convert xml response to java objects using jaxb
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://SOMETHING/doc/2006-03-01/", local:"Name"). Expected elements are <{}Name>,<{}IsTruncated>,<{}MaxKeys>,<{}Contents>,<{}Prefix>,<{}Marker>
Here is my XML :
<ListBucketResult xmlns="http://something/doc/2006-03-01/">
<Name>test2</Name>
<Prefix/>
<Marker/>
<MaxKeys>3</MaxKeys>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>metadata.xml</Key>
<LastModified>2012-09-04T08:29:36.000Z</LastModified>
<ETag>6b836fd43c402681506926b2248ec418</ETag>
<Size>3258</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
And my java object classes are something like this
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"name",
"prefix",
"marker",
"maxKeys",
"isTruncated",
"contents"
})
#XmlRootElement(name = "ListBucketResult")
public class ListBucketResult {
#XmlElement(name = "Name", required = true)
protected String name;
#XmlElement(name = "Prefix", required = true)
protected String prefix;
#XmlElement(name = "Marker", required = true)
protected String marker;
#XmlElement(name = "MaxKeys")
protected int maxKeys;
#XmlElement(name = "IsTruncated")
protected boolean isTruncated;
#XmlElement(name = "Contents", required = true)
protected ListBucketResult.Contents contents;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"key",
"lastModified",
"eTag",
"size",
"storageClass"
})
public static class Contents {
#XmlElement(name = "Key", required = true)
protected String key;
#XmlElement(name = "LastModified", required = true)
#XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar lastModified;
#XmlElement(name = "ETag", required = true)
protected String eTag;
#XmlElement(name = "Size")
protected int size;
#XmlElement(name = "StorageClass", required = true)
protected String storageClass;
and finally my unmarshalling code is :
JAXBContext jc = JAXBContext.newInstance(ListBucketResult.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
JAXBElement element = (JAXBElement) unmarshaller.unmarshal (inputStream);
ListBucketResult customer = (ListBucketResult) element.getValue();
Could someone please let me know what am i doing incorrect ?
You can use the #XmlSchema annotation on a package-info class to control the namespace qualification. If you have already written a package-info class make sure it is being compiled (some versions of ant had problems with package-info classes).
package-info
#XmlSchema(
namespace = "http://something/doc/2006-03-01/",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
The XML document contains elements which are part of the 'http://something/doc/2006-03-01/' namespace, but the annotated java class is not associated with that namespace. Try changing the #XmlType annotation to:
#XmlType(name = "", namespace="http://something/doc/2006-03-01/", propOrder = { ...
I was getting similar error org.codehaus.jackson.JsonParseException and javax.xml.bind.UnmarshalException : with linked exception: [javax.xml.bind.UnmarshalException: unexpected element (uri:"sometext", local:"elementA"). Expected elements are .
This is when integrating RESTEasy and Spring with my DTO's generated using JAXB. I was using Jackson for conversion.
To solve it I introduced the Jackson library in the Maven dependency and it solved my problem. The jackson dependency that is specific to this was
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.9</version>
</dependency>
There were other Jackson jars as well which I needed, so my POM Essentially looked as below.
//The jackson-core-asl jar contains streaming JSON parser and generator interfaces and implementations
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.9</version>
</dependency>
//Ability to use JAXB annotations containing classes needed to add XML compatibility support.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.9</version>
</dependency>
//Mapper jar contains functionality for data binding:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
</dependency> //This dependency makes a JAX-RS implementation like Jersey,RESTEasy use Jackson for binding of JSON to-from Java objects
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.9</version>
</dependency>