Serialize XML element having property named value using Jackson - java

I'm trying to deserialize/serialize xml content with below element.
<?xml version="1.0" encoding="utf-8" ?>
<confirmationConditions>
<condition type="NM-GD" value="something">no modification of guest details</condition>
</confirmationConditions>
How can I properly create java beans with jackson annotations to parse this correctly. I've tried with JAXB annotations and jackson fails saying it can't be having to value fields. With below java beans I got following error.
public class Condition
{
#JacksonXmlProperty( isAttribute = true, localName = "type" )
private String type;
#JacksonXmlProperty( isAttribute = true, localName = "value" )
private String value;
private String text;
}
Error
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class Condition), not marked as ignorable (3 known properties: "value", "type", "text"])
at [Source: (File); line: 3, column: 73] (through reference chain: ConfirmationConditions["condition"]->Condition[""])
Basically what I want is to map element content to text field. I have no control over xml so changing it would not work for me.

The thing you need here is to add #JacksonXmlText
class Condition {
#JacksonXmlProperty(isAttribute = true)
private String type;
#JacksonXmlProperty(isAttribute = true)
private String value;
#JacksonXmlText
private String text;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
And parse it this way:
JacksonXmlModule module = new JacksonXmlModule();
module.setDefaultUseWrapper(false);
XmlMapper xmlMapper = new XmlMapper(module);
xmlMapper.readValue(
"<condition type=\"NM-GD\" value=\"something\">no modification of guest details</condition>", Condition.class);

Related

jackson xmlmapper dynamic attribute names

I want to generate following xml:
<word start="1556" end="1564" TestArticle="36" Chemical="7">Ammonium</word>
<word start="1566" end="1584" Endpoint="36" Chemical="7" >per-fluorobutyrate</word>
<word start="1585" end="1586" TestArticle="37" >(</word>
I am using following pojo, which takes care of start and end as they are fixed attribute names, but I have "testArticle", "Endpoint", "Chemcial", etc which are dynamic in nature as well as there value, which I am not sure how to handle.
public class WordPOJO {
#JacksonXmlProperty(isAttribute = true)
String start;
#JacksonXmlProperty(isAttribute = true)
String end;
#JacksonXmlText
String str;
// not sure if this is the way to do it, but it not outputing in desired format
#XmlAnyElement(lax = true)
List<String> entityList;
public String getStart() {
return start;
}
public void setStart(String span) {
this.start = span;
}
public String getEnd() {
return end;
}
public void setEnd(String span) {
this.end = span;
}
public List<String> getEntityList()
{
return entityList;
}
public void setEntityList(List<String> entityList)
{
this.entityList = entityList;
}
//#JacksonXmlElementWrapper(useWrapping = false)
//#JacksonXmlProperty(localName = "word")
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
If you know the XML attribute names TestArticle , Chemical and Endpoint in adavance,
then you can represent these as additional properties in your WordPOJO Java class:
#JacksonXmlProperty(isAttribute = true, localName = "TestArticle")
String testArticle;
#JacksonXmlProperty(isAttribute = true, localName = "Chemical")
String chemical;
#JacksonXmlProperty(isAttribute = true, localName = "Endpoint")
String endpoint;
// Getters and setters (omitted here for brevity)
Notice that you also need to explicitly specify the XML names
by localName = "...") to match your XML content.
(See also the javadoc of #JacksonXmlProperty.)
If you would not do this, then Jackson would implicitly
pick up XML names derived from your Java property names
(testArticle, chemical, endpoint) which would not match
your actual XML content.
If the XML attributes are truly dynamic and you don't know them in advance,
then you will need to use #JsonAnyGetter and #JsonAnySetter.
See the javadoc of #JsonAnyGetter and JsonAnySetter.
Don't be irritated by Json in the annotation names. Jackson can handle JSON and XML and many more formats. For JSON you use ObjectMapper.
And for XML you use XmlMapper.
Map<String, Object> otherProperties = new HashMap<>();
#JsonAnySetter
public void setOtherProperty(String name, String value) {
otherProperties.put(name, value);
}
#JsonAnyGetter
public Map<String, Object> getOtherProperties() {
return otherProperties;
}

Jackson: deserialize with Builder along with standard setters/getters?

Is it possible with Jackson to deserialize json with Builder pattern as well as with default setter and getter approach?
My object is created with Builder that covers only required (final) fields, but I have non-final fields with some values as well that need to be deserialized with setters.
Here is the sample that throws an exception in an attempt to deserialize it with:
new ObjectMapper().readValue(json, Foo.class);
json - json representation serialized with default Jackson serializer, like:
objectMapper.writeValueAsString(foo);
class
#Getter
#Setter
#ToString
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonDeserialize(builder = Foo.Builder.class)
public class Foo {
private final String key;
private final Long user;
private final String action;
private final String material;
private final String currency;
private Foo(String key, Long user, String action, String material, String currency) {
this.key = key;
this.user = user;
this.action = action;
this.material = material;
this.currency = currency;
}
public static class Builder {
private String key;
private Long user;
private String action;
private String material;
private String currency;
#JsonProperty("key")
public Foo.Builder withKey(String key) {
this.key = key;
return this;
}
#JsonProperty("user")
public Foo.Builder withUser(Long user) {
this.user = user;
return this;
}
#JsonProperty("action")
public Foo.Builder withAction(String action) {
this.action = action;
return this;
}
/// other 'with' setters....
}
#JsonProperty("state")
private int state;
#JsonProperty("stat")
private String stat;
#JsonProperty("step")
private String step;
}
The exception it throws like :
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "state" (class com.Foo$Builder), not marked as
ignorable (5 known properties: "key", "user", "action", "material",
"currency",])
If not possible what workaround is the cheapest?
Two things that are suspicious:
You are willing to use the builder inside the Foo class. In that case you should correct the specification
(SessionData.Builder.class is not correct in that case).
You are indeed trying to use an external builder. In this case you should remove or at least mark as ignorable the inner builder, this seems to be the reason of the excetpion you are getting.
In both cases you should make sure the final method to get the Foo instance is called build() otherwise you should annotate the builder with a #JsonPOJOBuilder(buildMethodName = "nameOfMethod", withPrefix = "set").

XML to java Object conversion using simpleframework

I am converting xml file to java object using simpleframework. I am using generics since my xml's internal nodes gets changing. But simpleframework throws an error while converting. Here is my sample code:
#Root(name = "searchresult", strict = false)
public class ResponseVO<T>
{
#ElementList(entry = "document", inline = true)
public List<T> elementVOList = new ArrayList<T>();
public List<T> getElementsVOList()
{
return elementVOList;
}
public void setElementsVOList(List<T>list)
{
elementVOList = list;
}
}
ResponseVO is container for various other VOs as below:
#Root(name = "document", strict = false)
public class Projects_Display_VO
{
#Element(name = "projectname")
private String projectName;
#Attribute(name = "id")
private int tmpid;
public int getTmpid()
{
return tmpid;
}
public void setTmpid(int tmpid)
{
this.tmpid = tmpid;
}
/**
* ProjectId
*/
#Element(name = "projectid")
private String projectID;
public String getProjectName()
{
return projectName;
}
public void setProjectName(String projectName)
{
this.projectName = projectName;
}
public int getProjectID()
{
return Integer.parseInt(projectID);
}
public void setProjectID(String projectID)
{
this.projectID = projectID;
}
}
And the XML file is as below:
<searchresult>
<query>id:(PROJ2 PROJ6)</query>
<document id="0">
<projectid>2</projectid>
<projectname>Redundant Demo Project</projectname>
<doctype>Projects</doctype>
<summary>||Demo Project</summary>
<title>Redundant Demo Project</title>
</document>
<document id="1">
<projectid>6</projectid>
<projectname>Redundant Demo Project2</projectname>
<doctype>Projects</doctype>
<summary>||Main terminal links.</summary>
<title>Terminal 5 Project</title>
</document>
</searchresult>
The code for conversion is as bellow:
ResponseVO<Projects_Display_VO> resp = (ResponseVO<Projects_Display_VO>) SerializationUtil.deserialize(ResponseVO.class, reader);
Here i am using serializer from simpleframework. But it throws following error
Exception::Attribute 'id' does not have a match in class java.lang.Object at line 5
org.simpleframework.xml.core.AttributeException: Attribute 'id' does not have a match in class java.lang.Object at line 5
at org.simpleframework.xml.core.Composite.readAttribute(Composite.java:555)
at org.simpleframework.xml.core.Composite.readAttributes(Composite.java:474)
at org.simpleframework.xml.core.Composite.readSection(Composite.java:387)
I dont know whats going wrong here.A it works fine without generics.
Thanks in advance
This is caused by erasure, T is not available at runtime. Java does not allow it.

Using JAXB annotations with RestEasy

I'm trying to use JAXB annotations with RestEasy in order to choose names and elements order in my JSON output.
Somehow, it isn't working, even if the RestEasy doc says it's possible.
Here some code:
#XmlRootElement(name = "translation")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "translation", propOrder = {
"key",
"value"
})
public class TranslationDTO {
public TranslationDTO() {}
public TranslationDTO(Translation translation) {
setKey(translation.getTranslationKey().getValue());
setValue(translation.getContent());
//setCreationDate(translation.getCreatedTimestamp());
}
#XmlElement(name = "key")
private String key;
#XmlElement(name = "value")
private String value;
//private Date creationDate;
#XmlElement(name = "key")
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
#XmlElement(name = "value")
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
/*#XmlElement(name = "creationDate")
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}*/
}
And here an example output:
{
"name":"i18nhelp",
"currentVersion":"1",
"currentTotalKeys":28,
"oldTotalKeys":0,
"totalLanguages":2,
"languageDtos":[{
"name":"Anglais",
"iso639":"en",
"totalCurTrans":28,
"newCurTrans":28,
"oldTrans":0
},
{
"name":"Français",
"iso639":"fr",
"totalCurTrans":28,
"newCurTrans":28,
"oldTrans":0
}]
}
The JAXB annotations don't seem to be taken in account at all.
Any idea will be considered...
If you are using JBoss (or WildFly as it's now called) as an application server, you may be experiencing RestEasy using the Jackson (http://jackson.codehaus.org/) JSON marshaller, which has its own annotations – you can find the documentation linked from Jackson's homepage. They are a bit more expressive than "just" JAXB, you may want to consider them if you specifically target JSON output only.
If you would rather only use JAXB, as you example indicates, you can switch from Jackson to something different by specifying which resteasy provider module you want to use in a jboss-deployment-structure.xml, as detailed in this answer.

Problem Deserializing with Jackson using JAXB annotations in Spring MVC

I'm having trouble getting Jackson to correctly deserialize json into an object when calling a service (specifically we're using Jackson's ability to use JAXB annotations since we also want the service to use XML). I'm using Spring MVC and I'm using the RestTemplate class to make calls to the service.
Here is where I setup the MappingJacksonHttpMessageConverter for my junit:
ObjectMapper jsonMapper = new ObjectMapper();
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
jsonMapper.getDeserializationConfig().setAnnotationIntrospector(introspector);
jsonMapper.getSerializationConfig().setAnnotationIntrospector(introspector);
jsonMapper.getSerializationConfig().setSerializationInclusion(Inclusion.NON_NULL);
MappingJacksonHttpMessageConverter jacksonConverter = new MappingJacksonHttpMessageConverter();
jacksonConverter.setObjectMapper(jsonMapper);
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(jacksonConverter);
template.setMessageConverters(converters);
And I call the service like so:
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/json");
HttpEntity<String> requestEntity = new HttpEntity<String>(requestHeaders);
ResponseEntity<NamedSystem> responseEntity = template.exchange(baseURL + "/{NamedSystemId}",
HttpMethod.GET, requestEntity, NamedSystem.class, orgId1);
My NamedSystem class is set up like so:
#XmlRootElement(name = "NamedSystem", namespace = "http://schemas.abc.workplace.com/NamedSystem")
public class NamedSystem {
private String id;
private String name;
private String description;
private Set<NamedSystemAlias> aliases;
private String href;
#XmlAttribute(required = false, name = "id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlAttribute(required = false, name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlAttribute(required = false, name = "description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#XmlElementWrapper(required = false, name = "aliases", namespace = "http://schemas.abc.workplace.com/NamedSystem")
#XmlElement(required = false, name = "alias", namespace = "http://schemas.abc.workplace.com/NamedSystem")
public Set<NamedSystemAlias> getAliases() {
return aliases;
}
public void setAliases(Set<NamedSystemAlias> aliases) {
this.aliases = aliases;
}
#XmlAttribute(required = true, name = "href")
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
}
This is the error that results:
org.springframework.web.client.ResourceAccessException: I/O error: Unrecognized field "NamedSystem" (Class com.workplace.abc.named.NamedSystem), not marked as ignorable
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#c1429c; line: 1, column: 2]; nested exception is org.codehaus.jackson.map.JsonMappingException: Unrecognized field "NamedSystem" (Class com.workplace.abc.named.NamedSystem), not marked as ignorable
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#c1429c; line: 1, column: 2]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:453)
....
Caused by: org.codehaus.jackson.map.JsonMappingException: Unrecognized field "NamedSystem" (Class com.workplace.abc.named.NamedSystem), not marked as ignorable
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#c1429c; line: 1, column: 2]
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:159)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:247)
at org.codehaus.jackson.map.deser.StdDeserializer.reportUnknownProperty(StdDeserializer.java:366)
at org.codehaus.jackson.map.deser.StdDeserializer.handleUnknownProperty(StdDeserializer.java:352)
at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:543)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:402)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:287)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:1588)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1172)
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.java:132)
at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:154)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:74)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:619)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:446)
... 32 more
It seems it doesn't recognize the rootElement 'NamedSystem' to be able to deserialize. How would I get it to do that? I've seen examples that use the same JAXB annotations and they work fine so I'm not sure what's different about my case or how I might force it to correctly deserialize it. If anyone can offer any help, I'd appreciate it.
If anyone comes along this kind of problem, this might fix it for you: Enable Jackson to not output the class name when serializing (using Spring MVC)
See my answer and follow the link for an example.

Categories