Deserialize Child XML Elements as an XML String - java

Using either Jackson or JAXB, is there a way to deserialize/marshal an entire subtree of child XML elements into a String?
For example, given the following XML:
<root>
<foo>
<bar>
<baz/>
</bar>
</foo>
</root>
Is there a way to deserialize/marshal that to the following class
#XmlRootElement
public class Root {
private String foo;
// constructors, getters, setters, etc.
}
where the result would look like Root(foo="<bar><baz/></bar>")?

You can write your own jackson deserializer for this:
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new XmlMapper();
final SimpleModule module = new SimpleModule("configModule", Version.unknownVersion());
module.addDeserializer(Root.class, new DeSerializer());
mapper.registerModule(module);
// Root readValue = mapper.readValue(<xml source>);
}
}
class DeSerializer extends StdDeserializer<Root> {
protected DeSerializer() {
super(Root.class);
}
#Override
public Root deserialize(JsonParser p, DeserializationContext ctxt) throws Exception {
// use p.getText() and p.nextToken to navigate through the xml and construct Root object
return new Root();
}
}

Related

Jackson include a key that is empty string

I accept from a server a json like this:
{
"": "hello"
}
And in Jackson I did
#JsonProperty("")
private String string
When deserialising the object it ignores the property completely.
How can I make an empty string count as a key?
Thank you
I found a way to achieve what you want with custom deserializer by following steps.
Step 1: Create the POJO class you want to deserialize to
public class MyPojo {
private String emptyFieldName;
//constructor, getter, setter and toString
}
Step 2: Create your custom deserializer
public class MyDeserializer extends StdDeserializer<MyPojo> {
public MyDeserializer () {
this(null);
}
protected MyDeserializer (Class<?> vc) {
super(vc);
}
#Override
public MyObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
String emptyFieldName = jsonNode.get("").asText();
return new MyPojo(emptyFieldName);
}
}
Step 3: Register this custom deserializer
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(MyPojo.class, new MyDeserializer());
objectMapper.registerModule(module);
MyPojo myPojo = objectMapper.readValue(jsonStr, MyPojo.class);
System.out.println(myPojo.getEmptyFieldName());
Console output:
hello
BTW, you could also directly register this custom deserializer on the class:
#JsonDeserialize(using = MyDeserializer.class)
public class MyPojo {
...
}
For more information, please refer to Getting Started with Custom Deserialization in Jackson.

Adding Custom deserialization class to already exsisting #JsonDeserialize annotation using jackson

I am trying to deserialize json by writing custom deserializer.
Here is my code.
public class EventLoginDeserializer extends StdDeserializer<EventLogin> {
public EventLoginDeserializer() {
this(null);
}
public EventLoginDeserializer(Class<EventLogin> event) {
super(event);
}
#Override
public EventLogin deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
return EventLogin.builder().displayName(jsonNode.get("display_name"))
.timestamp(DateTime.now()).build();
}
#Override
public Class<EventLogin> handledType() {
return EventLogin.class;
}
}
And here is the snippet of my main class.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule module = new SimpleModule();
module.addDeserializer(EventLogin.class, new EventLoginDeserializer());
mapper.registerModule(module);
String json = "{\"display_name\": \"Test Deserialization\", \"user_name\": \"test\"}";
EventLogin eventLogin = mapper.readValue(json, EventLogin.class);
System.out.println("readValue :::: " + eventLogin);
I have a requirement wherein I've to take an already existing #JsonDeserialize annotated model class in the jar file and add one more deserialization class above. I meant here is the same of an already existing class in a source file.
#AutoValue
#JsonDeserialize(builder = AutoValue_EventLogin.Builder.class)
#JsonInclude(JsonInclude.Include.NON_NULL)
public abstract class EventLogin
{
public abstract String displayName();
public abstract DateTime timestamp();
#AutoValue.Builder
public abstract static class Builder implements Login.Builder<Builder> {
public abstract Builder displayName(String displayName);
public abstract Builder emailId(DateTime timestamp);
public abstract EventLogin build();
}
}
The problem is that since #JsonDeserialize already exists in the jar file, so adding custom deserializer was not being considered at all. Meaning, the overridden deserialize method the custom deserializer class is not being executed.
So how to overcome this problem?
Go the solution. Jackson lets us override those attribute-specified values with mixins, so we could create a mixin class and add in the object mapper.
#JsonDeserialize(using = EventLoginDeserializer.class)
public static class EventLoginMixIn{}
and then in our main class
ObjectMapper mapperWithMixin = new ObjectMapper()
.addMixIn(EventLogin.class, EventLoginMixIn.class);
// Deserialize the eventlogin
EventLogin eventLogin = mapperWithMixin.readValue(actualJson, EventLogin.class);

JSON to Java object deserialization with escaped properties

I need to convert the following JSON to Java object. The property providerResponse in the JSON contains map of properties but they are escaped and wrapped in doubleQuotes. As a result, it does not deserialize the property providerResponse into a Java object (it comes as String). I use objectMapper.readValue(msgStr, classType) to deserialize the JSON. The message is generated by AWS for SNS delivery status notifications and I don't have control to change the JSON message. Is it possible to configure ObjectMapper to unescape the property and deserialize into a Java object instead of String?
{
"delivery":{
"providerResponse":"{\"sqsRequestId\":\"308ee0c6-7d51-57b0-a472-af8e6c41be0b\",\"sqsMessageId\":\"88dd59eb-c34d-4e4d-bb27-7e0d226daa2a\"}"
}
}
#JsonProperty("providerResponse")
private String providerResponse;
There doesn't seem to be a way to configure ObjectMapper to handle this behavior by default. The solution is to create a custom JsonDeserializer:
public class Wrapper {
public Delivery delivery;
}
public class Delivery {
#JsonDeserialize(using = ProviderResponseDeserializer.class)
public ProviderResponse providerResponse;
}
public class ProviderResponse {
public String sqsRequestId;
public String sqsMessageId;
}
public class ProviderResponseDeserializer extends JsonDeserializer<ProviderResponse> {
private static final ObjectMapper mapper = new ObjectMapper();
#Override
public ProviderResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return mapper.readValue(jsonParser.getText(), ProviderResponse.class);
}
}
Then you can deserialize the JSON by using your ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
Wrapper wrapper = mapper.readValue(JSON, Wrapper.class);
I faced this similar issue. This gets resolved if we define a constructor in ProviderResponse which takes a single string argument (which is actually json) and then map the json in the constructor to the instance of ProviderResponse and use this temp instance to initialise the properties.
public class Wrapper {
public Delivery delivery;
}
public class Delivery {
public ProviderResponse providerResponse;
}
public class ProviderResponse {
public String sqsRequestId;
public String sqsMessageId;
private static ObjectMapper objMapper = new ObjectMapper();
public ProviderResponse(String json) {
ProviderResponse temp = objMapper.readValue(json, ProviderResponse.class);
this.sqsMessageId = temp.sqsMessageId;
this.sqsRequestId = temp.sqsRequestId;
}
}
The key is to keep the ObjectMapper instance and the its usage somewhere in your utility class and use it from there.

How to create a custom deserializer in Jackson for a generic type?

Imagine the following scenario:
class <T> Foo<T> {
....
}
class Bar {
Foo<Something> foo;
}
I want to write a custom Jackson deserializer for Foo. In order to do that (for example, in order to deserialize Bar class that has Foo<Something> property), I need to know the concrete type of Foo<T>, used in Bar, at deserialization time (e.g. I need to know that T is Something in that particluar case).
How does one write such a deserializer? It should be possible to do it, since Jackson does it with typed collections and maps.
Clarifications:
It seems there are 2 parts to solution of the problem:
1) Obtain declared type of property foo inside Bar and use that to deserialize Foo<Somehting>
2) Find out at deserialization time that we are deserializing property foo inside class Bar in order to successfully complete step 1)
How does one complete 1 and 2 ?
You can implement a custom JsonDeserializer for your generic type which also implements ContextualDeserializer.
For example, suppose we have the following simple wrapper type that contains a generic value:
public static class Wrapper<T> {
public T value;
}
We now want to deserialize JSON that looks like this:
{
"name": "Alice",
"age": 37
}
into an instance of a class that looks like this:
public static class Person {
public Wrapper<String> name;
public Wrapper<Integer> age;
}
Implementing ContextualDeserializer allows us to create a specific deserializer for each field in the Person class, based on the generic type parameters of the field. This allows us to deserialize the name as a string, and the age as an integer.
The complete deserializer looks like this:
public static class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {
private JavaType valueType;
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
WrapperDeserializer deserializer = new WrapperDeserializer();
deserializer.valueType = valueType;
return deserializer;
}
#Override
public Wrapper<?> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
Wrapper<?> wrapper = new Wrapper<>();
wrapper.value = ctxt.readValue(parser, valueType);
return wrapper;
}
}
It is best to look at createContextual here first, as this will be called first by Jackson. We read the type of the field out of the BeanProperty (e.g. Wrapper<String>) and then extract the first generic type parameter (e.g. String). We then create a new deserializer and store the inner type as the valueType.
Once deserialize is called on this newly created deserializer, we can simply ask Jackson to deserialize the value as the inner type rather than as the whole wrapper type, and return a new Wrapper containing the deserialized value.
In order to register this custom deserializer, we then need to create a module that contains it, and register that module:
SimpleModule module = new SimpleModule()
.addDeserializer(Wrapper.class, new WrapperDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
If we then try to deserialize the example JSON from above, we can see that it works as expected:
Person person = objectMapper.readValue(json, Person.class);
System.out.println(person.name.value); // prints Alice
System.out.println(person.age.value); // prints 37
There are some more details about how contextual deserializers work in the Jackson documentation.
If the target itself is a generic type then property will be null, for that you'll need to get the valueTtype from the DeserializationContext:
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
if (property == null) { // context is generic
JMapToListParser parser = new JMapToListParser();
parser.valueType = ctxt.getContextualType().containedType(0);
return parser;
} else { // property is generic
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
JMapToListParser parser = new JMapToListParser();
parser.valueType = valueType;
return parser;
}
}
This is how you can access/resolve {targetClass} for a Custom Jackson Deserializer. Of course you need to implement ContextualDeserializer interface for this.
public class WPCustomEntityDeserializer extends JsonDeserializer<Object>
implements ContextualDeserializer {
private Class<?> targetClass;
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
//Your code here to customize deserialization
// You can access {target class} as targetClass (defined class field here)
//This should build some {deserializedClasObject}
return deserializedClasObject;
}
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property){
//Find here the targetClass to be deserialized
String targetClassName=ctxt.getContextualType().toCanonical();
try {
targetClass = Class.forName(targetClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return this;
}
}
For my use case, none of the above solutions worked, so I had to write a custom module. You can find my implementation on GitHub.
I wanted to write a deserializer that automatically removes blank Strings from Lists.

How to deserialize a blank JSON string value to null for java.lang.String?

I am trying a simple JSON to de-serialize in to java object. I am however, getting empty String values for java.lang.String property values. In rest of the properties, blank values are converting to null values(which is what I want).
My JSON and related Java class are listed below.
JSON string:
{
"eventId" : 1,
"title" : "sample event",
"location" : ""
}
EventBean class POJO:
public class EventBean {
public Long eventId;
public String title;
public String location;
}
My main class code:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
try {
File file = new File(JsonTest.class.getClassLoader().getResource("event.txt").getFile());
JsonNode root = mapper.readTree(file);
// find out the applicationId
EventBean e = mapper.treeToValue(root, EventBean.class);
System.out.println("It is " + e.location);
}
I was expecting print "It is null". Instead, I am getting "It is ". Obviously, Jackson is not treating blank String values as NULL while converting to my String object type.
I read somewhere that it is expected. However, this is something I want to avoid for java.lang.String too. Is there a simple way?
Jackson will give you null for other objects, but for String it will give empty String.
But you can use a Custom JsonDeserializer to do this:
class CustomDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.readValueAsTree();
if (node.asText().isEmpty()) {
return null;
}
return node.toString();
}
}
In class you have to use it for location field:
class EventBean {
public Long eventId;
public String title;
#JsonDeserialize(using = CustomDeserializer.class)
public String location;
}
It is possible to define a custom deserializer for the String type, overriding the standard String deserializer:
this.mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new StdDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String result = StringDeserializer.instance.deserialize(p, ctxt);
if (StringUtils.isEmpty(result)) {
return null;
}
return result;
}
});
mapper.registerModule(module);
This way all String fields will behave the same way.
You might first like to see if there has been any progress on the Github issue requesting this exact feature.
For those using Spring Boot: The answer from jgesser was the most helpful to me, but I spent a while trying to work out the best way to configure it in Spring Boot.
Actually, the documentation says:
Any beans of type com.fasterxml.jackson.databind.Module are
automatically registered with the auto-configured
Jackson2ObjectMapperBuilder and are applied to any ObjectMapper
instances that it creates.
So here's jgesser's answer expanded into something you can copy-paste into a new class in a Spring Boot application
#Configuration
public class EmptyStringAsNullJacksonConfiguration {
#Bean
SimpleModule emptyStringAsNullModule() {
SimpleModule module = new SimpleModule();
module.addDeserializer(
String.class,
new StdDeserializer<String>(String.class) {
#Override
public String deserialize(JsonParser parser, DeserializationContext context)
throws IOException {
String result = StringDeserializer.instance.deserialize(parser, context);
if (StringUtils.isEmpty(result)) {
return null;
}
return result;
}
});
return module;
}
}
I could get this by following configuration.
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
it is possible to use JsonCreator annotation. It worked for me
public class Foo {
private String field;
#JsonCreator
public Foo(
#JsonProrerty("field") String field) {
this.field = StringUtils.EMPTY.equals(field) ? null : field ;
}
}

Categories

Resources