Genson Polymorphic / Generic Serialization - java

I am trying to implement a JSON serialization in Java with Genson 1.3 for polymorphic types, including:
Numbers
Arrays
Enum classes
The SSCCE below demonstrates roughly what I am trying to achieve:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.owlike.genson.Genson;
import com.owlike.genson.GensonBuilder;
/**
* A Short, Self Contained, Compilable, Example for polymorphic serialization
* and deserialization.
*/
public class GensonPolymoprhicRoundTrip {
// our example enum
public static enum RainState {
NO_RAIN,
LIGHT_RAIN,
MODERATE_RAIN,
HEAVY_RAIN,
LIGHT_SNOW,
MODERATE_SNOW,
HEAVY_SNOW;
}
public static class Measurement<T> {
public T value;
public int qualityValue;
public String source;
public Measurement() {
}
public Measurement(T value, int qualityValue, String source) {
this.value = value;
this.qualityValue = qualityValue;
this.source = source;
}
}
public static class DTO {
public List<Measurement<?>> measurements;
public DTO(List<Measurement<?>> measurements) {
this.measurements = measurements;
}
}
public static void main(String... args) {
Genson genson = new GensonBuilder()
.useIndentation(true)
.useRuntimeType(true)
.useClassMetadataWithStaticType(false)
.addAlias("RainState", RainState.class)
.useClassMetadata(true)
.create();
DTO dto = new DTO(
new ArrayList(Arrays.asList(
new Measurement<Double>(15.5, 8500, "TEMP_SENSOR"),
new Measurement<double[]>(new double[] {
2.5,
1.5,
2.0
}, 8500, "WIND_SPEED"),
new Measurement<RainState>(RainState.LIGHT_RAIN, 8500, "RAIN_SENSOR")
)));
String json = genson.serialize(dto);
System.out.println(json);
DTO deserialized = genson.deserialize(json, DTO.class);
}
}
Numbers and Arrays worked well out-of-the-box, but the enum class is providing a bit of a challenge. In this case the serialized JSON form would have to be IMO a JSON object including a:
type member
value member
Looking at the EnumConverter class I see that I would need to provide a custom Converter. However I can't quite grasp how to properly register the Converter so that it would be called during deserialization. How should this serialization be solved using Genson?

Great for providing a complete example!
First problem is that DTO doesn't have a no arg constructor, but Genson supports classes even with constructors that have arguments. You just have to enable it via the builder with 'useConstructorWithArguments(true)'.
However this will not solve the complete problem. For the moment Genson has full polymorphic support only for types that are serialized as a json object. Because Genson will add a property called '#class' to it. There is an open issue for that.
Probably the best solution that should work with most situations would be to define a converter that automatically wraps all the values in json objects, so the converter that handles class metadata will be able to generate it. This can be a "good enough" solution while waiting for it to be officially supported by Genson.
So first define the wrapping converter
public static class LiteralAsObjectConverter<T> implements Converter<T> {
private final Converter<T> concreteConverter;
public LiteralAsObjectConverter(Converter<T> concreteConverter) {
this.concreteConverter = concreteConverter;
}
#Override
public void serialize(T object, ObjectWriter writer, Context ctx) throws Exception {
writer.beginObject().writeName("value");
concreteConverter.serialize(object, writer, ctx);
writer.endObject();
}
#Override
public T deserialize(ObjectReader reader, Context ctx) throws Exception {
reader.beginObject();
T instance = null;
while (reader.hasNext()) {
reader.next();
if (reader.name().equals("value")) instance = concreteConverter.deserialize(reader, ctx);
else throw new IllegalStateException(String.format("Encountered unexpected property named '%s'", reader.name()));
}
reader.endObject();
return instance;
}
}
Then you need to register it with a ChainedFactory which would allow you to delegate to the default converter (this way it works automatically with any other type).
Genson genson = new GensonBuilder()
.useIndentation(true)
.useConstructorWithArguments(true)
.useRuntimeType(true)
.addAlias("RainState", RainState.class)
.useClassMetadata(true)
.withConverterFactory(new ChainedFactory() {
#Override
protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) {
if (Wrapper.toAnnotatedElement(nextConverter).isAnnotationPresent(HandleClassMetadata.class)) {
return new LiteralAsObjectConverter(nextConverter);
} else {
return nextConverter;
}
}
}).create();
The downside with this solution is that useClassMetadataWithStaticType needs to be set to true...but well I guess it is acceptable as it's an optim and can be fixed but would imply some changes in Gensons code, the rest still works.
If you are feeling interested by this problem it would be great you attempted to give a shot to that issue and open a PR to provide this feature as part of Genson.

Related

How to modify Json body before receiving?

I receive different objects set from the API. Each response have a follow structure:
items:[
{
user_id:1,
tags: {..}
},
{..}
]
The problem is that I do not want so unuseful and not readable structure.
I mean, all my methods (I use Retrofit library) must have some next signature:
Call<UserRepresantation>...
Call<RepoRepresentation>...
instead
Call<List<Users>>
Call<List<Repos>>
And also I have to use additional entities every time:
class UserRepresentation{
List<Users> items;
}
The Retrofite has possibility to use different converters for the serialization, for example:
Retrofit.Builder()
.baseUrl(stckUrl)
.addConverterFactory(GsonConverterFactory.create(new Gson())) < --- converter applying
.build();
As I understand I can use JsonSeializer to configure such behavior, but I can't figure out in which way. Can anyone help me to solve this issue?
So, in the simple words:
we have a response:
items:[
{
user_id:1,
tags: {..}
},
{..}
]
And we need to receive:
List<Users> = gson.fromJson(respose, User.class);
One solution would be to write a TypeAdapterFactory which performs the unwrapping when asked to deserialize any List<User> and List<Repo>, or in general for any List. However, the problem with this is that it would also apply to any nested lists of these types, for example when your User class has a field List<Repo> repos then that adapter factory would also try to unwrap its value, and fail.
So a more reliable solution might be to implement a TypeAdapterFactory which keeps track of whether it is currently being used to deserialize the top-level value and in that case unwrap / flatten the data. If not used for the top-level value it could simply let the other registered adapter factories handle the data:
class FlatteningTypeAdapterFactory implements TypeAdapterFactory {
public static final FlatteningTypeAdapterFactory INSTANCE = new FlatteningTypeAdapterFactory();
private FlatteningTypeAdapterFactory() { }
/** Tracks whether this is a nested call to this factory */
private static final ThreadLocal<Boolean> isNestedCall = new ThreadLocal<>();
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// Only handle top-level value, for nested calls let other factory handle it
// Uses Boolean.TRUE.equals to handle case where value is `null`
if (Boolean.TRUE.equals(isNestedCall.get())) {
return null;
}
TypeAdapter<T> delegate;
isNestedCall.set(true);
try {
delegate = gson.getDelegateAdapter(this, type);
} finally {
isNestedCall.remove();
}
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) {
throw new UnsupportedOperationException();
}
#Override
public T read(JsonReader in) throws IOException {
in.beginObject();
String name = in.nextName();
if (!name.equals("items")) {
throw new IllegalArgumentException("Unexpected member name: " + name);
}
T value;
// While using delegate adapter also set isNestedCall in case delegate looks up
// another adapter dynamically while its `read` method is called
isNestedCall.set(true);
try {
value = delegate.read(in);
} finally {
isNestedCall.remove();
}
in.endObject();
return value;
}
};
}
}
You would then have to register it with a GsonBuilder before constructing the GsonConverterFactory:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(FlatteningTypeAdapterFactory.INSTANCE)
.create();
Note that the code above has not been extensively tested; there might be bugs or corner cases where it does not work correctly.

GSON Serialize Polymorphic Object with Type Stored in a Different Object

To start off, I have looked at a few other answers for similar questions, but they do not answer my particular situation.
I'm parsing JSON messages which consist of a body and a header, where the header stores what type of object the body is:
{
"body": {
"eventName": "someEventName"
},
"header": {
"purpose": "event"
}
}
In Java, I've modeled this structure using the following classes:
public class Message {
public Body body;
public Header header;
}
public class Header {
public String purpose; // Marks what child class the body of the message uses
}
public abstract class Body {
// Child classes store additional fields
}
// Example implementation of the body class
public class EventBody extends Body {
public String eventName; // Name of some event
}
After doing some research, I found that RuntimeTypeAdapterFactory is normally used to parse/write polymorphic objects; however, the RutimeTypeAdapterFactory class relies on the type being stored in the base class of the polymorphic object (i.e. Body). But in this scenario, that's not the case ― the type is stored in another object, Header.
What would be the best way to go about parsing these kind of objects? I'd like to avoid having to write a custom Serializer/Deserializer for compactness, but I wouldn't mind writing them if it's necessary.
I realize that asking for a solution that doesn't involve a custom Serializer/Deserializer is a bit ridiculous, as this is exactly the type of scenario they'd be used in (I was thinking I could get away with a custom TypeAdapterFactory, but using a Serializer/Deserializer is easier).
Anyway, for my scenario, a combination of a custom Serializer/Deserializer for the Message class seems to work fine. Since I already use an enum to track different message purposes and their string names, I decided to simply add an additional field to that enum to store the corresponding body class.
MessagePurpose Enum:
public enum MessagePurpose {
EVENT("event", EventBody.class);
public final String purposeName;
public final Class bodyClass;
MessagePurpose(String purposeName, Class classi) {
this.purposeName = purposeName;
bodyClass = classi;
}
}
MessageSerializer:
public class MessageSerializer implements JsonSerializer<Message> {
#Override
public JsonElement serialize(Message message, Type type, JsonSerializationContext jsc) {
if(message == null) {
return null;
}
JsonObject messageObj = new JsonObject();
// Get the class representing the body object from the purpose enum
Class bodyClassType = message.getPurpose().bodyClass;
messageObj.add("body", jsc.serialize(message.getBody(), bodyClassType));
messageObj.add("header", jsc.serialize(message.getHeader(), Header.class));
return messageObj;
}
}
MessageDeserializer:
public class MessageDeserializer implements JsonDeserializer<Message> {
#Override
public Message deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
Header header = jdc.deserialize(je.getAsJsonObject().get("header"), Header.class);
// Get the class representing the body object from the purpose enum
Class bodyClassType = header.getPurpose().bodyClass;
Body body = jdc.deserialize(je.getAsJsonObject().get("body"), bodyClassType);
return new Message(body, header);
}
}
Main function to test with:
public static void main(String[] args) {
GsonBuilder gb = new GsonBuilder();
// Register the Message class since I need to access info in the header
gb.registerTypeAdapter(Message.class, new MessageDeserializer());
gb.registerTypeAdapter(Message.class, new MessageSerializer());
Gson gson = gb.setPrettyPrinting().create();
EventBody event = new EventBody(EventType.SOME_EVENT_NAME);
String eventJson = gson.toJson(event.getAsMessage());
System.out.println(eventJson);
Message newEvent = gson.fromJson(eventJson);
System.out.println("\nEvent type: " + ((EventBody) newEvent.getBody()).getEventName());
}
The above test class prints:
{
"body": {
"eventType": "someEventName"
},
"header": {
"purpose": "event"
}
}
Event Type: someEventName
This output matches the JSON of the Messages I'm parsing, and it seems to deserialize different types of messages just fine.

Gson Java - Child class from JSON

I have an abstract class for configuration files, which can be extended by lots of other classes. I managed to get the system working for writing it to JSON, but now I need the load function.
Here's the general Configuration class:
public class Configuration {
public boolean load(){
FileReader reader = new FileReader(this.getClass().getSimpleName() + ".json");
Gson gson = new Gson();
gson.fromJson(reader, this.getClass());
reader.close();
/** Doesn't give an error, but doesn't set any info to the child class */
}
public boolean save(){
FileWriter writer = new FileWriter(this.getClass().getSimpleName() + ".json");
Gson gson = new Gson();
gson.toJson(this, writer);
writer.close();
/** This all works fine. */
}
}
Here's an example of an extending class:
public class ExampleConfig extends Configuration {
private static transient ExampleConfig i = new ExampleConfig();
public static ExampleConfig get() { return i; }
#Expose public String ServerID = UUID.randomUUID().toString();
}
In my main class I would do:
ExampleConfig.get().load();
System.out.println(ExampleConfig.get().ServerID);
This does not give any errors, but neither is the class loaded from the JSON. It keeps outputting a random UUID even though I want to load one from the JSON file. I'm probably getting the wrong instance of the child class, but I'm out of ideas on how to fix this. (Using this in gson.fromJson(.....); does not work.
You're missing to assign a read value to your configuration instance. Java cannot support anything like this = gson.fromJson(...), and Gson can only return new values and cannot patch existing ones. The below is a sort of Gson hack, and please only use it if it's really a must for you. Again, I would strongly recommend you to redesign your code and separate your configuration objects and configuration readers/writers -- these are just two different things that conflict from the technical perspective. As a result of refactoring, you could have, let's say, once you get an instance of your configuration, just delegate it to a writer to persist it elsewhere. If you need it back, then just get an instance of a reader, read the configuration value and assign it to your configuration (configurations are singletons, I remember), like:
final ConfigurationWriter writer = getConfigurationWriter();
writer.write(ExampleConfig.get());
...
final ConfigurationReader reader = getConfigurationReader();
ExampleConfig.set(reader.read(ExampleConfig.class));
At least this code does not mix two different things, and makes the result of reader.read be explicitly read and assigned to your configuration singleton.
If you're fine to open the gate of evil and make your code work because of hacks, then you could use Gson TypeAdapterFactory in order to cheat Gson and patch the current configuration instance.
abstract class Configuration {
private static final Gson saveGson = new Gson();
public final void load()
throws IOException {
try ( final FileReader reader = new FileReader(getTargetName()) ) {
// You have to instantiate Gson every time (unless you use caching strategies) in order to let it be *specifically* be aware of the current
// Configuration instance class. Thus you cannot make it a static field.
final Gson loadGson = new GsonBuilder()
.registerTypeAdapterFactory(new TypeAdapterFactory() {
// A Gson way to denote a type since Configuration.class may not be enough and it also works with generics
private final TypeToken<Configuration> configurationTypeToken = new TypeToken<Configuration>() {
};
#Override
#SuppressWarnings("deprecation") // isAssignableFrom is deprecated
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Checking if the type token represents a parent class for the given configuration
// If yes, then we cheat...
if ( configurationTypeToken.isAssignableFrom(typeToken) ) {
// The map that's artificially bound as great cheating to a current configuration instance
final Map<Type, InstanceCreator<?>> instanceCreators = bindInstance(typeToken.getType(), Configuration.this);
// A factory used by Gson internally, we're intruding into its heart
final ConstructorConstructor constructorConstructor = new ConstructorConstructor(instanceCreators);
final TypeAdapterFactory delegatedTypeAdapterFactory = new ReflectiveTypeAdapterFactory(
constructorConstructor,
gson.fieldNamingStrategy(),
gson.excluder(),
new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor)
);
// Since the only thing necessary here is to define how to instantiate an object
// (and we just give it an already-existing instance)
// ... just delegate the job to Gson -- it would think as if it's creating a new instance.
// Actually it won't create one, but would "patch" the current instance
return delegatedTypeAdapterFactory.create(gson, typeToken);
}
// Otherwise returning a null means looking up for an existing type adapter from how Gson is configured
return null;
}
})
.create();
// The value is still loaded to nowhere, however.
// The type adapter factory is tightly bound to an existing configuration instance via ConstructorConstructor
// This is actually another code smell...
loadGson.fromJson(reader, getClass());
}
}
public final void save()
throws IOException {
try ( final FileWriter writer = new FileWriter(getTargetName()) ) {
saveGson.toJson(this, writer);
}
}
private String getTargetName() {
return getClass().getSimpleName() + ".json";
}
private static Map<Type, InstanceCreator<?>> bindInstance(final Type type, final Configuration existingConfiguration) {
return singletonMap(type, new InstanceCreator<Object>() {
#Override
public Object createInstance(final Type t) {
return t.equals(type) ? existingConfiguration : null; // don't know if null is allowed here though
}
});
}
}
I hope that the comments in the code above are exhaustive. As I said above, I doubt that you need it just because of intention to have a bit nicer code. You could argue that java.util.Properties can load and save itself. Yes, that's true, but java.util.Properties is open to iterate over its properties by design and it can always read and write properties from elsewhere to anywhere. Gson uses reflection, a method of peeking the fields under the hood, and this is awesome for well-designed objects. You need some refactoring and separate two concepts: the data and data writer/reader.

Using Gson to elegantly handle nested json objects?

I'm using Gson to parse responses from a server on Android. Each response has some useless (to me) data on it that complicates my Gson models. Here is the general hierarchy of json returned:
response: {
date: 1406253006807,
otherUselessData1: "This is some useless data",
otherUselessData2: "This is some useless data",
usefulJsonObject: { <---- This is really the object that I care about
}
}
Everything above or at the same level as usefulJsonObject I could really do without. The useless data is returned for every request, and the actual response is embedded beneath as the usefulJsonObject. This wouldn't be a big problem but it's really cluttering up my gson model objects.
For example:
Let's say I have 3 requests I can make: A, B, and C. For each response it seems I need to make a minimum of 3 custom classes.
public class ResponseA {
#SerializedName("response") ResponseObjectA responseObject;
public static class ResponseObjectA {
#SerializedName("usefulJsonObject") UsefulObjectA usefulObject;
}
public static class UsefulObjectA {
}
}
I've tried a few solutions, but I haven't found anything elegant that wouldn't add an extra step to my process. I'm using retrofit to do my http requests and it's really nice that it just returns the fully parsed gson object to me. I've thought of other solutions like having the useful object just be a JsonElement and then doing a 2nd gson call after the first comes back. Again, not ideal.
I just wanted to know if I was missing something. Surely I'm not the only one who's encountered something like this, and so I thought I'd ask how other people would handle something like this.
It is initialization Instance value, not NULL value. Check my example.
Address.java
public class Address {
public Address(){
}
}
Person.java
public class Person {
private String name;
private String nrc;
private Address address;
public Person(String name, String nrc, Address address) {
this.name = name;
this.nrc = nrc;
this.address = address;
}
}
The following Json string is equalvent to
Person person = new Person("Zaw Than Oo", "11111", null);
{
"name": "Zaw Than Oo",
"nrc": "11111"
}
The following Json string is equalvent to
Person person = new Person("Zaw Than Oo", "11111", new Address());
{
"name": "Zaw Than Oo",
"nrc": "11111",
"address": {} <-- here use less object for you.
}
Even if you don't create new Instance, Other lib/api(you used) may be create that instance by Reflection.
Short to the Point
{
...
"xxx": {} --> new instance without data/value
...
}
{
...
--> null value
...
}
I never found an elegant way dealing with just Gson. I tried several options with Generics, all of which didn't work or left something to be desired.
Since I'm using Retrofit, I decided to override the GsonConverter, and just filter out the unnecessary information from all my requests. It ends up not being as flexible, as in I can't use the same Retrofit network interface for calls to other servers, but I'm not really doing that, and it also has the down side of having 2 rounds of json parsing calls (meh). You could probably do this more efficiently, but this is working for me for now.
public class CustomGsonConverter extends GsonConverter {
private Gson mGson;
public CustomGsonConverter(Gson gson) {
super(gson);
this.mGson = gson;
}
public CustomGsonConverter(Gson gson, String encoding) {
super(gson, encoding);
this.mGson = gson;
}
#Override public Object fromBody(TypedInput body, Type type) throws ConversionException {
try {
CustomResponse customResponse = mGson.fromJson(new InputStreamReader(body.in()), CustomResponse.class);
return mGson.fromJson(customResponse.responseObject.data, type);
} catch (IOException e) {
throw new ConversionException(e);
}
}
public static class CustomResponse {
#SerializedName("rsp") ResponseObject responseObject;
public static class ResponseObject {
// #SerializedName("date") long date;
#SerializedName("data") JsonElement data;
}
}
}
Maybe there is a better way that I'm just not realizing.

Serialization third-party classes with Simple XML (org.simpleframework.xml)

I have decided to use Simple XML serialization and was stucked with basic problem. I am trying to serialize java.util.UUID class instance as final field in this small class:
#Root
public class Identity {
#Attribute
private final UUID id;
public Identity(#Attribute UUID id) {
this.id = id;
}
}
Tutorial shows how to serialize third-party objects by registering converters like this:
Registry registry = new Registry();
registry.bind(UUID.class, UUIDConverter.class);
Strategy strategy = new RegistryStrategy(registry);
Serializer serializer = new Persister(strategy);
serializer.write( object, stream );
appropriate converter for UUID is pretty simple:
public class UUIDConverter implements Converter<UUID> {
#Override
public UUID read(InputNode node) throws Exception {
return new UUID.fromString(node.getValue());
}
#Override
public void write(OutputNode node, UUID value) throws Exception {
node.setValue(value.toString());
}
}
But this simple code just didn't work for me, during serialization objects with UUID fields was thrown exception Transform of class java.util.UUID not supported.
I have tried something something similar with custom Matcher (which was not in tutorial) that works for me:
Serializer serializer = new Persister(new MyMatcher());
serializer.write( object, stream );
and Matcher class looks like this:
public static class MyMatcher implements Matcher {
#Override
#SuppressWarnings("unchecked")
public Transform match(Class type) throws Exception {
if (type.equals(UUID.class))
return new UUIDTransform();
return null;
}
}
public class UUIDTransform implements Transform<UUID> {
#Override
public UUID read(String value) throws Exception {
return UUID.fromString(value);
}
#Override
public String write(UUID value) throws Exception {
return value.toString();
}
}
Questions:
Is custom Matcher always recommended practice for streaming third-party classes?
In which case I can use Converter?
Are there any better tutorials/examples for Simple XML out there?
Thank you.
I have to answer by myself again :-)
Advice from Niall Gallagher, project leader of Simple XML, from support-list:
"You could use either a Converter or a Transform. I would say
for a UUID a Transform with a Matcher would be the easiest option."
So, I use Transform<T>/Matcher and satisfied with it. This does not alter the fact that the Converter<T> does not work for me :-)
I think i have the answer to this.
Strategy strategy = new AnnotationStrategy();
Serializer serializer = new Persister(strategy);
should register the converter and solve the problem.
I know this is a bit aold but my chance i came to the same exception.
The actual issue is the use of the #Attribute annotation. If instead of #Attribute
you put #Element the exception does not appear and the converter is used for the serialization.
I guess it will then depend on which annotation you used that you should create a Converter or use the Marker-Transform solution. Although i do not know if this is the intendent behaviour.

Categories