How to deserialize abstract classes in jackson - java

I am trying to deserialize a JSON object to a Java object using jackson. The json was created using the jackson library itself by another developer and is provided to me. The Object graph has a variable of Type guava's ImmutableMap which is an abstract class.Hence jackson throws me the following exception when I try to deSerialize. I am not the owner of the myJava class and hence couldn't make any changes to it like changing the type or adding annotations by writing a custom deserializer.
org.codehaus.jackson.map.JsonMappingException: Can not find a
deserializer for non-concrete Map type [map type; class
com.google.common.collect.ImmutableMap, [simple type, class
com.walmart.services.common.enums.ShipMethod] -> [simple type, class
com.walmart.services.shipprice.api.DetailedShipPrice]]
Thanks

When dealing with Guava types, you will want to use Guava datatype module (https://github.com/FasterXML/jackson-datatype-guava). It is not a problem of just having abstract type; but Guava also uses builder-style instead of constructors.

Related

Deserialize JSON for a class I dont own with custom getters

I am trying to deserialize an object of a class that I do not own. The class has attribute names such as id_, address_, name_, but its getters are getId() getAddress() getName() etc.
When I try to deserialize the JSON using Jackson, I get
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "id_", not marked as ignorable
It looks like this happens because Jackson's looking for getId_() instead of getId(). Since I do not own the underlying class, I cannot use Jackson's annotations to map attributes to custom json fields.
How can I deserialize with a custom mapping of object attributes to its getter methods?
You can try a custom deserializer.
check out: https://www.baeldung.com/jackson-deserialization
This way you can register a deserializer for the class.
However, you still have to edit it when the class changes.
One other thing you can try:
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
This will give the mapper full access to private members.
You can apply a MixIn for Jackson - that's how I solved my issue trying to serialize and deserialize JSON for an auto-generated AVRO class (Avro generated class issue with json conversion [kotlin])
Here is an example:
https://medium.com/#shankar.ganesh.1234/jackson-mixin-a-simple-guide-to-a-powerful-feature-d984341dc9e2

#JsonTypeInfo with Spring Data Rest results in serialization error

I am currently trying a scenario with EclipseLink, spring-data-jpa and spring-data-rest where I have Embeddable classes with inheritance.
The scenario is rather simple: A Parent contains a value that can ether be a PercentageValue or an AbsoluteValue.
The mapping:
The Parent holds an embeddable value:
#Entity
public class Parent {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Embedded
private Value value;
}
Value is the abstract superclass for the different values
#Embeddable
#Customizer(Customizers.ValueCustomizer.class)
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
#JsonSubTypes({
#Type(name="PERCENTAGE", value=PercentageValue.class),
#Type(name="ABSOLUTE", value=AbsoluteValue.class)})
public abstract class Value {}
PercentageValue is an example for a concrete Value implementation
#Embeddable
#Customizer(Customizers.PercentageValueCustomizer.class)
#JsonSerialize(as = PercentageValue.class)
public class PercentageValue extends Value {
private BigDecimal percentageValue;
}
Using EclipseLink customizers I can get the inheritance with embeddables to work but spring-data-rest does not seem to be able to serialize the value objects because of the type information.
A GETrequest on the parents resource results in the following exception:
.w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Type id handling not implemented for type java.lang.Object (by serializer of type org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$NestedEntitySerializer) (through reference chain: org.springframework.data.rest.webmvc.json.["content"]->com.example.Parent["value"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type java.lang.Object (by serializer of type org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$NestedEntitySerializer) (through reference chain: org.springframework.data.rest.webmvc.json.["content"]->com.example.Parent["value"])
It seems that NestedEntitySerializer is not implementing com.fasterxml.jackson.databind.JsonSerializer#serializeWithType falling back to the standard implementation that just throws an exception.
If I remove the #JsonTypeInfo annotation the serialization works but of course the POST fails because Jackson is lacking the type information for proper deserialization.
Any thoughts on that? Is there a way to make the serialization work with JsonTypeInfo?
The complete project is available on GitHub.
Spring Data REST has a number of documented limitations when it comes to supporting Jackson's #JsonTypeInfo when serializing data to the REST output.
Currently, it appears that #JsonTypeInfo(include=JsonTypeInfo.As.EXISTING_PROPERTY) works (spring-data-rest#1242), but the other JsonTypeInfo.As types do not (spring-data-rest#1269).
It should be possible to migrate your model to use JsonTypeInfo.As.EXISTING_PROPERTY instead of JsonTypeInfo.As.PROPERTY.

How to deserialize interface fields using Jackson's objectMapper?

ObjectMapper's readValue(InputStream in, Class<T> valueType) function requires the Class. But how do I use it if the class I am passing internally, is having some Interface as data member.
although I can understand the reason behind this exception, as Jackson is not getting the concrete class of the internal Interface of the passed class, but my question is how to resolve it?
how do I deserialize it then? The class I am trying to deserialize is:
class BaseMetricImpl<N> implements Metric<N> {
protected MetricValueDescriptor descriptor;
}
Here MetricValueDescriptor is an interface, so this gives me following error : -
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of MetricValueDescriptor, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
at [Source: java.io.ByteArrayInputStream#2ede2c9f; line: 1, column: 2] (through reference chain: SingleValueMetricImpl["descriptor"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:624)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:115)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2793)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1989)
Jackson obviously cannot construct the MetricValueDescriptor object since it is an interface. You will need to have additional information in your json and in your ObjectMapper to tell jackson how to construct an object out of it. Here is one way to do it, assuming MVDImpl is a concrete class which implements MetricValueDescriptor:
You can tell Jackson the required type information through a field in the json itself, say "type". To do this, you need to use JsonTypeInfo and JsonSubTypes annotations in your interface. For example,
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = MVDImpl.class, name = "mvdimpl") })
interface MetricValueDescriptor
{
...
}
You will need to add a "type":"mvdimpl" field in your json as well.
I was going to point you to the official doc for more info, but then I found an excellent blog covering this topic - Deserialize JSON with Jackson. It covers this topic pretty comprehensively and with examples. So you should definitely read it if you need more customisation.
I see it going one of two ways, but they both require you manually create a concrete class that implements your interface.
Use #Hari Menon's answer and use #JsonSubTypes. This works if you can introduce a type field or something else to trigger which implementation to use.
Use #JsonDeserialize to tell jackson what concrete class it uses by default.
#JsonDeserialize(as = MVDImpl.class)
interface MetricValueDescriptor
{
...
}
Here's a more thorough explanation: https://zenidas.wordpress.com/recipes/jackson-deserialization-of-interfaces/
And the docs: https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html
You don't need to alter the code, you can set it programmatically on the mapper:
static setup() {
final var simpleModule = new SimpleModule()
.addAbstractTypeMapping(<Interface>.class, <Implementation>.class);
objMapper = new ObjectMapper()
.registerModule(new Jdk8Module()) // You probably want this as well
.registerModule(simpleModule);
}

Serialize object using GSON

How can I serialize and deserialize this object with gson to json:
public class test{
#Expose
public List< Pair<Double,Double> > list;
#Expose
public int alpha;
}
I've tried this:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String str = gson.toJson(testInstance,test.class);
where testInstance is an instance of class test, but it's not working because of Pair structure in List.
You have configured Gson object using excludeFieldsWithoutExposeAnnotation method.
From documentation:
Configures Gson to exclude all fields from consideration for
serialization or deserialization that do not have the Expose
annotation.
Could you change Pair class? If yes, you have to add #Expose annotation to each property which you want serialize to JSON. If no, you can create similar class in your program and add annotations.
if the object is of a generic type, then the Generic type information is lost because of Java Type Erasure. You can solve this problem by specifying the correct parameterized type for your generic type.
Try parameterized type like List<String>. This should work

Is there a JSON library that can serialize Proxy objects?

Using ActiveObjects as my ORM and Gson as my JSON processor.
Ran into a problem going toJson from
persisted objects. The problem is that my persisted class is actually
an Interface and AO is proxying that object under the hood. Here's
some sample code:
Venue venue = manager.get(Venue.class, id);
gson.toJson(venue);
Comes up with this exception:
java.lang.UnsupportedOperationException: Expecting parameterized type,
got interface java.lang.reflect.InvocationHandler.
Are you missing the use of TypeToken idiom?
See http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and...
Because venue.getClass().getName() gives:
$Proxy228
I've tried a few solutions in various combinations:
gsonBuilder.registerTypeAdapter(Venue.class, newVenueSerializer());
Type listType = new TypeToken<Venue>() {}.getType();
Nothing has worked so far and I'm using a wonky field-by-field workaround. Any suggestions? I'm not married to Gson, so if there's an alternative library that can do this I'd be happy to use it.
Flex JSON should work - it will use the bean property introspector to pull the object, and I assume the proxy class implements those properly.
Also check out the Jackson.

Categories

Resources