Deserialize subclasses with Jackson by parent class field value - java

I have a parent class:
open class NetworkMessage(var type: NetworkMessageType)
and a bunch of it's subclasses, like
class ConnectionAcceptedResponseMessage(
val accepted: Boolean,
val uid: String
) : NetworkMessage(NetworkMessageType.CONNECTION_ACCEPTED)
so every message type is clearly determined by parent class field value. Is there any way to deserialize concrete subclass using only annotations and without using treeNode?

In Java, you can do something like this on the parent class:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = ConnectionAcceptedResponseMessage.class, name = "CONNECTION_ACCEPTED"),
#JsonSubTypes.Type(value = OtherResponseMessage.class, name = "OTHER")
})
I guess in Kotlin this will be similar.

Related

How do I unmarshal a dynamic type from JSON with Spring RestTemplate and Jackson

I have a JSON response coming from an API that is calling Elastic Search. The following is a sample snippet.
{
"fielda" : "something",
"hits" : {
"total" : "100",
"type" : "/some/type"
"dynamicAttributes":{
"somevalue1" : "somevalue",
"somevalue2" : "somevalue2"
}
}
}
The JSON subtree underneath the dynamicAttributes can be different with each result. I am trying to marshal that into an object based on the type field that comes in. There will be an established mapping between that value and a class somewhere in a map. It looks like Jackson maps the results into a LinkedHashMap if the type is not resolved. I'm looking to use a Custom Deserializer. Is this the best strategy for this? Or is there something simpler that I'm missing.
You can use #JsonTypeInfo and #JsonSubType annotations to marshal the given input.
On your dynamicAttributes field you can add a JsonTypeInfo annotation to specify how to create a dynamicAttribute's object. The EXTERNAL_PROPERTY type conveys that the type field is present at the level of dynamicAttributes.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property ="type", include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
visible = true)
DynamicAttributes dynamicAttributes;
To specify the possible subtypes use the JsonSubTypes annotation as shown below
#JsonSubTypes({
#JsonSubTypes.Type(value = DynamicAttributesType1.class, name = "type1")
, #JsonSubTypes.Type(value = DynamicAttributesType2.class, name = "type2")
})
public abstract class DynamicAttributes{
}

Casting an object to limit data serialized in Jackson serialization

Let's say I have the following classes:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type",
visible = true
)
#JsonSubTypes(
JsonSubTypes.Type(FullXResult::class, name = "X")
)
open class BaseResult {
protected val mapper: ObjectMapper = ObjectMapper()
var x: String = "some shared text between all subclasses"
}
open class MinimalXResult: BaseResult() {
var y: String = "some public text specific to X"
}
class FullXResult: MinimalXResult() {
var z: String = "some private text specific to X"
fun asMinimal(): MinimalXResult {
return mapper.convertValue(this, MinimalXResult::class.java)
}
}
I want to be able to deserialize a JSON into the FullResult, and before re-serialization, cast it to a MinimalResult to the caller such that for the example classes, the properties x and y are serialized but the property z is not. #JsonIgnore is not an option here because I also want to be able to return the full result under certain conditions.
When calling asMinimal function, I get the following error:
Could not resolve type id 'X' as a subtype of `MinimalXResult`: Class `FullXResult` not subtype of `MinimalXResult`
If this type of casting can be done without Jackson, that is fine too. Thanks for any help.

MapStruct, map inheritance

I have 2 entities Vehicle, which is an interface, and VehicleDto, a class.
Vehicle has 2 subtypes: Car and Truck. All 3 of them have a field name.
VehicleDto has 2 fields Car and Truck, and only one of those may have a value.
How do I map the subtypes Car and Truck to the class VehicleDto so that the appropriate field of the VehicleDto is filled in with the name value of the subtype?
Currently I have this:
#Mapping(source = "name", target = "Car")
#Mapping(source = "name", target = "Truck")
VehicleDto mapToVehicle(Vehicle vehicle);
But that obviously does not work because now both Car and Truck fields of VehicleDto are filled in.
I also tried something like this
#Mapping(source = "name", target = "Car")
VehicleDto mapToVehicle(Car car);
#Mapping(source = "name", target = "Truck")
VehicleDto mapToVehicle(Truck truck);
But that also doesn't work properly.
What's a good way to implement this behavior? The reference guide of MapStruct was not very clear to me.

Json to object deserialization issue in Graphql-spqr

Json to GraphQLArgumetn object conversion failing in graphql-spqr.
tried adding GraphQLInterface(with autodiscovery true and scanpackage) to above abstract classes
and GraphQLtype type all concrete classes.
My graph query:
query contactsQuery($searchQuery : QueryInput) { contacts(searchQuery:$searchQuery){id}}
variables:{"searchQuery":{"bool":{"conditions":[{"must":{"matches":[{"singleFieldMatch":{"boost":null,"field":"firstname","value":"siddiq"}}],"bool":null}}]}})
Java code:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({#type(value = Must.class, name="must"),#type(value = MustNot.class, name="mustNot")})
public abstract class Condition
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({#type(value = SingleFieldMatch.class, name="singleFieldMatch"),#type(value = MultiFieldMatch.class, name="multiFieldMatch")})
public abstract class Match
#GraphQLQuery(name = "contacts")
public List getContacts(#GraphQLArgument(name ="searchQuery") Query query)
Still it's throwing error unknown field error etc. Not sure which configuration is missing.
Building GraphQLSchema with AnnotatedResolvedBuilder, base package configured JacksonValueMappperFactory and singleton services.
Hi this may be a similar issue to what I ended up having.
Initially I had the following
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#GraphQLInterface(name = "AbstractClass", implementationAutoDiscovery = true)
public abstract class AbstractClass{
with the following query called
addNewObject(object: {name: "soft2", id: "asdas"})
To get conversion functioning what I needed to do was the following change
#JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type")
#GraphQLInterface(name = "AbstractClass", implementationAutoDiscovery = true)
public abstract class AbstractClass{
private String type = this.getClass().getSimpleName();
/**
* #return the type
*/
#GraphQLQuery(name = "type", description = "The concrete type of the node. This should match the initialised class. E.g. \"Concrete\", \"DecafCoffee\"")
public String getType() {
return type;
}
with the query now being
addNewConcreteObject(concrete: {name: "soft2", id: "asdas", type: "Concrete"})
Why this worked (I think):
When converting from JSON to objects in my code using the Jackson converter (ObjectMapper). I had previously noticed that the JSON required knowledge of what class to convert to. Thus the initial use of #JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type") put a type property in the JSON when it was written to string.
The inclusion of the #JSON tag may be picked up by SPQR and it then seems to use a Jackson converter to try to convert your query to the required object.
If I am right, here is the issue.
As the query doesn't contain type the query can not be correctly converted. Moreover as the type property was not a member variable of the object but was instead only added by the ObjectMapper, SPQR didn't pick it up and so it wasn't part of the schema for the object. Thus to get around it, I added type as a member variable which is always equal to the actual class, then changed my JsonTypeInfo to look for an existing property.
I appreciate this isn't a direct answer to your question (and definitely isn't a pretty answer), but hopefully it will help you find your solution.

Jackson parsing object of many probabilities but same property name

I'm using Jackson library in my Android project for parsing json responses.
In my case, the returned json array consists of objects of different structure each. I've created 3 VO classes, but I wonder if it is possible to tell jackson to try them on by one until any success.
The following could solve the problem, but it's forbidden, the same #JsonProperty but many possibilities:
#JsonProperty("Object")
public Class1 object1;
#JsonProperty("Object")
public Class2 object2;
#JsonProperty("Object")
public Class3 object3;
Any alternatives?
I would suggest to write a custom deserializer. You can find information on that here: http://www.baeldung.com/jackson-deserialization
Official docs are here: http://wiki.fasterxml.com/JacksonHowToCustomDeserializers
Alternative:
If the 3 objects are related to each other by implementing a common interface, you can do this:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
public interface MyObjectInterface {}
But then you will need to adjust the JSON to include the #class property. You did not state if that is possible or not.
Alternative 2:
If you already have a type property, you can do this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
#JsonSubTypes(
{#JsonSubTypes.Type(value = MyObject1.class, name = "OBJECT_1"),
#JsonSubTypes.Type(value = MyObject2.class, name = "OBJECT_2"),
#JsonSubTypes.Type(value = MyObject3.class, name = "OBJECT_3")}
)
public interface MyObjectInterface {}

Categories

Resources