#XMLElementWrapper issue with codehaus JacksonJaxbJsonProvider - java

I am trying to get the correct JSON for
public class MyTestResponse {
#XmlElementWrapper(name = "data")
#XmlElement(name = "values")
public List<String> test = Arrays.asList("Sidney");
}
I now get
"data": [
"Sidney"
],
instead of
"data":{
"values": [
"Sidney"
]
},
I am using org.codehaus.jackson stack (1.9.0) inside ServiceMix 7 M3.
My JSON provider extends org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider:
import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
public class MyJsonProvider extends JacksonJaxbJsonProvider {
public JsonProvider() {
super();
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector primary = new JaxbAnnotationIntrospector();
AnnotationIntrospector secondary = new JacksonAnnotationIntrospector();
AnnotationIntrospector pair = new AnnotationIntrospector.Pair(secondary, primary);
mapper.getDeserializationConfig().setAnnotationIntrospector(pair);
mapper.getSerializationConfig().setAnnotationIntrospector(pair);
this.setMapper(mapper);
}
}
How can I tell the JacksonJaxbJsonProvider not to replace the XmlElement name but tow wrap it?

As you have discovered JSON marshalling doesn’t honor JAXB annotations. Enable the following MapperFeature on your jackson ObjectMapper (your instance named mapper)
mapper.enable(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME);

Related

Jackson serialization of immutable value class with optional fields

I am using Immutables library (https://immutables.github.io).
My class looks as follows:
package com.abc.myservice.data.models;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
import java.util.Map;
import java.util.Optional;
#Value.Immutable
#JsonSerialize(as = ImmutableMyEntityModel.class)
#JsonDeserialize(as = ImmutableMyEntityModel.class)
public interface MyEntityModel {
String myEntityId();
String status();
Optional<Integer> count();
Optional<Integer> version();
Optional<Map<String, String>> attributes();
}
I build the immutable class object with:
ImmutableMyEntityModel.builder()
.myEntityId("some-id")
.status("some-status")
.count(Optional.of(10))
.build()
And my output is:
{
"MyEntityId": "some-id",
"status": "some-status",
"count": {
"present": true
},
"version": {
"present": false
},
"attributes": {
"present": false
}
}
Instead what I would like to see is:
{
"MyEntityId": "some-id",
"status": "some-status",
"count": 10
}
How can I make it work like that?
Use the jackson-datatype-jdk8 module so that Jackson properly understands the java.util.Optional type - a pretty good explanation is in this article.
Add jackson-datatype-jdk8 library to your project/classpath, which contains a Jackson module that allows Jackson to properly understand Optionals.
When creating an ObjectMapper, register the Jdk8Module:
ObjectMapper om = new ObjectMapper();
om.registerModule(new Jdk8Module());
Optionally, add #JsonIgnoreProperties(ignoreUnknown = true) to properties or the class itself to avoid serializing Optional.empty() to null values and instead ignore the property completely.
Full example:
public class JacksonOptionalTest
{
public static void main(String... args)
throws Exception
{
ObjectMapper om = new ObjectMapper();
om.registerModule(new Jdk8Module());
Thing thing = new Thing();
thing.name = "John Smith";
thing.count = Optional.of(12);
String s = om.writeValueAsString(thing);
System.out.println(s);
}
#JsonInclude(Include.NON_ABSENT)
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Thing
{
public String name;
public Optional<Integer> count = Optional.empty();
public Optional<Integer> version = Optional.empty();
}
}
The output of this is {"name":"John Smith","count":12}.

JaxbDto Serialization and deserialization

I need to receive some message with SOAP so I've generated a few classes by xsd-scheme and maven-jaxb2-plugin like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Claim", propOrder = {
"field",
})
public class ClaimType {
#XmlElement(required = true, type = Integer.class, nillable = false)
protected Integer field;
public Integer getField() {
return bpType;
}
public void setField(Integer value) {
this.field= value;
}
}
After receiving message I need to send these to the next one microservice in wrap of HashMap.
I supposed to use ObjectMapper to convert:
//JAXB DTO --> JSON
ObjectMapper objectMapper = new ObjectMapper();
String jsonContent = objectMapper.writeValueAsString(claimType);
map.put("json", jsonContent);
//JSON --> JAXB DTO
ObjectMapper objectMapper = new ObjectMapper();
String json = map.get("json");
ClaimType claimType = objectMapper.readValue(json, ClaimType.class);
But the generated classes are haven't any constructors so I got the exception like "
No creator like default constructor are exists".
What is the best preactice to work with Jaxb Dto? Can I do smth to successful convert these json to object? Thanks in advance!
I've solved my problem by using ObjectMapper MixIn:
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
#JsonIgnoreProperties(value = {"globalScope", "typeSubstituted", "nil"})
public abstract class JAXBElementMixIn<T> {
#JsonCreator
public JAXBElementMixIn(#JsonProperty("name") QName name,
#JsonProperty("declaredType") Class<T> declaredType,
#JsonProperty("scope") Class scope,
#JsonProperty("value") T value) {
}
}
And the convertation:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(JAXBElement.class, JAXBElementMixIn.class);
solution link

How to convert a JsonNode instance to an actual pojo

At a certain point in my code, I have parse a JSON document, represented as a string, to a JsonNode, because I don't know yet the actual target pojo class type.
Now, some time later, I know the Class instance of the pojo and I want to convert this JsonNode to an actual pojo of that class (which is annotated with the proper #JsonProperty annotations). Can this be done? If so, how?
I am working with Jackson 2.10.x.
In this case you can use two methods:
treeToValue
convertValue
See below example:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.StringJoiner;
public class JsonNodeConvertApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonFile);
System.out.println(mapper.treeToValue(node, Message.class));
System.out.println(mapper.convertValue(node, Message.class));
}
}
class Message {
private int id;
private String body;
// getters, setters, toString
}
Above code for JSON payload like below:
{
"id": 1,
"body": "message body"
}
prints:
Message[id=1, body='message body']
Message[id=1, body='message body']

Can you use both #XmlElement and #JsonProperty together inside a POJO class

I have a json payload and an xml payload, and I want to map both of the payloads into one POJO class. One endpoint returns a json and the other a xml. Can i combine both into one pojo class.
{
"house": 'big',
"has-large-house": "yes"
}
<completed-houses>
.....
</completed-houses>
public PayloadResponse(
#JsonProperty("house") final String house,
#JsonProperty("has-large-house") final String hasLargeHouseList,
#XmlElement(name="completed-houses") final String completeHouses) {
this.house = house;
this.hasLargeHouseList = hasLargeHouseList;
this.completeHouses = completeHouses;
}
and then getters and setter for these properties.
Yes! You can combine Jackson and JAXB annotations in the same POJO, using the Jackson module for JAXB annotations so that Jackson can understand JAXB annotations, and jackson-dataformat-xml for serializing to XML.
Here is an example:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.xml.bind.annotation.XmlElement;
import java.io.IOException;
import java.io.StringWriter;
#Data
#NoArgsConstructor
public class PayloadResponse {
private String house;
#JsonProperty("has-large-house")
private boolean largeHouse;
#XmlElement(name = "completed-houses")
private String completedHouses;
public static void main(String[] args) throws IOException {
ObjectMapper xmlMapper = new XmlMapper();
JaxbAnnotationModule module = new JaxbAnnotationModule();
xmlMapper.registerModule(module);
PayloadResponse response = new PayloadResponse();
response.setHouse("The White House");
response.setLargeHouse(true);
response.setCompletedHouses("1600 Pennsylvania Ave.");
StringWriter stringWriter = new StringWriter();
// Serialize value as XML.
xmlMapper.writeValue(stringWriter, response);
System.out.println("XML=" + stringWriter);
// Serialize value as JSON.
ObjectMapper jsonMapper = new ObjectMapper();
stringWriter.getBuffer().setLength(0);
jsonMapper.writeValue(stringWriter, response);
System.out.println("JSON=" + stringWriter);
}
}
Outputs the following:
XML=<PayloadResponse>
<house>The White House</house>
<has-large-house>true</has-large-house>
<completed-houses>1600 Pennsylvania Ave.</completed-houses>
</PayloadResponse>
JSON={"house":"The White House",
"completedHouses":"1600 Pennsylvania Ave.",
"has-large-house":true}

#XMLElementWrapper issue with com.fasterxml.jackson JacksonJaxbJsonProvider

I am trying to get the correct JSON for
public class MyTestResponse {
#XmlElementWrapper(name = "data")
#XmlElement(name = "values")
public List<String> test = Arrays.asList("Sidney");
}
I now get
"values": [
"Sidney"
],
instead of
"data":{
"values": [
"Sidney"
]
},
So the wrapper element "data" is not present.
I am using com.fasterxml.jackson stack (2.8.6) inside ServiceMix 7 M3.
My JSON provider extends com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider:
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
#Provider
#Consumes({ MediaType.APPLICATION_JSON, "text/json" })
#Produces({ MediaType.APPLICATION_JSON, "text/json" })
public class JsonProvider extends JacksonJaxbJsonProvider {
public JsonProvider() {
super();
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector primary = new JaxbAnnotationIntrospector(mapper.getTypeFactory());
AnnotationIntrospector secondary = new JacksonAnnotationIntrospector();
AnnotationIntrospector pair = AnnotationIntrospector.pair(primary, secondary);
mapper.setAnnotationIntrospector(pair);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.disable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
mapper.disable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
this.setMapper(mapper);
}
}
How can I tell the JacksonJaxbJsonProvider to render the wrapper element around the values element?

Categories

Resources