If I just drop in .toJson and .fromJson instead of using .writeObject and .readObject, will I get identical results?
Additionally, what's the GSON equivalent of the readObject and writeObject methods that you can put in classes to be serialized?
Gson doesn't support inheritance or circular references sufficiently to be a drop-in replacement for Java Serialization.
For inheritance, use RuntimeTypeAdapterFactory to encode class names in JSON values.
For circular references, you can use GraphAdapterBuilder to encode values by reference rather than by value.
Neither of these files are included in the Gson distribution. You'll need to copy and paste them into your app if you want to use them.
Gson AFAIK does not have readObject & writeObject (according to it's API)
As for the second part of the question: GSON works like this: you can implement a serializer/deserializer like this:
private class DateTimeSerializer implements JsonSerializer<DateTime> {
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}
and then register them with gson.registerTypeAdapter(DateTime.class, DateTimeSerializer());
Related
I save the payload of events as JSON string into a database.
To only have one version of the events in code while still being able to read old events I want to "upcast" the serialized data before deserialization.
I thought about something like this:
public Object deserialize(String data, Class<?> clazz) throws IOException {
data = upcaster.upcast(data, clazz);
return objectMapper.readValue(data, clazz);
}
But this means I transform the string into some JSON object twice. Once for upcasting the data and once inside the standard jackson mapper.
Is there a way to customize jackson between building the json-object and building my event object?
Something like:
#override
updateData(clazz, jsonData) {
if(clazz.equals(SpecificEvent.class)) {
if(!jsonData.containsKey("addedInfo")) {
jsonData.put("addedInfo", "foo");
}
}
}
Alternatively, I could add my own deserializer that changes the given JSON data before calling the standard deserializer I guess?
What's the normal way of doing something like this with jackson?
One option is custom deserialization with a dedicated deserializer class (per your own class). You would then have to maintain the deserializer class to make sure that it provides any missing data.
It should work for a handful of classes, if you have a lot then there might be better ways.
I am using GSON to decode JSON into an object of type T e.g.
public T decode(String json) {
Gson gson = new Gson();
return gson.fromJson(json, new TypeToken<T>() {}.getType());
}
This however returns an exception -
java.lang.AssertionError: Unexpected type.
Expected one of:
java.lang.reflect.ParameterizedType,
java.lang.reflect.GenericArrayType,
but got: sun.reflect.generics.reflectiveObjects.TypeVariableImpl, for type token: T
I thought that by using TypeToken I avoided Type Erasure.
Am I wrong?
Thanks
First of all, I fail to see how it's useful to wrap Gson like that.
As to your problem, the information about generic type T itself is not available during runtime. It's been erased. It's only available during compile time. You want to parameterize it with the actual type instead like new TypeToken<List<String>>.
Due to lack of reified Generics in Java (it isn't possible to do a T t = new T()), Gson itself is forced to use the TypeToken approach, as you see. Otherwise Gson would have done it in a much more elegant manner.
In order to be able to pass the actual type around, you've to reinvent the same thing as TypeToken is already doing. And this makes no sense :) Just reuse it or just use Gson straight without wrapping it in some helper class like that.
I think the first answer is not pointing out the actual solution: you MUST also pass Class instance along with T, like so:
public T decode(String json, Class<T> cls) {
Gson gson = new Gson();
return gson.fromJson(json, cls);
}
This is because 'T' here is a type VARIABLE, not a type reference; and only used by compiler to add implicit casts and verify type compatibility. But if you pass actual class it can be used; and compiler will check type compatibility to reduce chance of mismatch.
Alternatively you could take in TypeToken and pass it; but TypeToken must be constructed with real type, not a type variable; type variable is of little use here. But if you do want to wrap things you wouldn't want caller to use TypeToken (which is a Gson type).
Same wrapping mechanism would work with other libs like Jackson, which you mentioned.
My solution to this was to use the json parser and break it into pieces
public static <TT> PushObj<TT> fromJSON(String json, Class<TT> classType)
{
JsonObject jObj = new JsonParser().parse(json).getAsJsonObject();
String type = jObj.get("type").getAsString();
JsonObject job = jObj.get("object").getAsJsonObject();
TT obj = new Gson().fromJson(job, classType);
return new PushObj<TT>(type, obj);
}
Where the object structure is:
{String:Type, Generic:Object}
And the variables are:
jObj is the JSONObject of the string passed in
and job is the JSONObject of the generic object
So i used the json parser to get the type separately, and reflection for the object.
I have a question regarding Singleton class behavior.
Scenario
We have webservices that is used by partners with request coming as frequently as 2-3 times in 1 second. So, they are heavily used services. We would like to store the request and response in JSON format.
As of now, in each webservice interface class, we have initiated Gson object and doing the serialization of java object to json.
Question
I was just thinking if I initiate Gson object once in Singleton class as static object and then refer it for all the request / response serialization of java object to Json, can it cause any issue/problem?
I mean, as there are multiple webservices and couple of them are heavily used by partners, so will single instance of GSON accessed in all the webservice can cause any delay or any other problem? If yes, apart than memory, what are the other benefits and issues with it?
From javadoc of Gson:
This is the main class for using Gson. Gson is typically used by first constructing a Gson instance and then invoking toJson(Object) or fromJson(String, Class) methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple threads.
So it's ok to have only one Gson instance in your service and reuse it for each request/response. As an example you can consider to create a util class, something like the following class:
public abstract class GsonUtils {
private static Gson gson = new Gson();
static {
// your extra init logic goes here
}
public static <T> T fromJson(String json, Class<T> classOfT) {
return gson.fromJson(json, classOfT);
}
public static String toJson(Object object) {
if (object == null) {
return "";
}
return gson.toJson(object);
}
}
Or you can do it in your way :-)
Recently I was dealing with same idea. There is older thread related to this Is it OK to use Gson instance as a static field in a model bean (reuse)?. Simply summarised the GSON instance should be thread safe.
I'm trying to marshal a list: List<Pojo> objects via the Spring Rest Template.
I can pass along simple Pojo objects, but I can't find any documentation that describes how to send a List<Pojo> objects.
Spring is using Jackson JSON to implement the HttpMessageConverter. The jackson documentation covers this:
In addition to binding to POJOs and
"simple" types, there is one
additional variant: that of binding to
generic (typed) containers. This case
requires special handling due to
so-called Type Erasure (used by Java
to implement generics in somewhat
backwards compatible way), which
prevents you from using something like
Collection<String>.class (which does
not compile).
So if you want to bind data into a
Map<String,User> you will need to use:
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() {});
where TypeReference is only needed to
pass generic type definition (via
anynomous inner class in this case):
the important part is
<Map<String,User>> which defines type
to bind to.
Can this be accomplished in the Spring template? I took a glance at the code and it makes me thing not, but maybe I just don't know some trick.
Solution
The ultimate solution, thanks to the helpful answers below, was to not send a List, but rather send a single object which simply extends a List, such as: class PojoList extends ArrayList<Pojo>. Spring can successfully marshal this Object, and it accomplishes the same thing as sending a List<Pojo>, though it be a little less clean of a solution. I also posted a JIRA in spring for them to address this shortcoming in their HttpMessageConverter interface.
In Spring 3.2 there is now support for generic types using the new exchange()-methods on the RestTemplate:
ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);
Works like a charm!
One way to ensure that generic type parameters are included is to actually sub-class List or Map type, such that you have something like:
static class MyStringList extends ArrayList<String> { }
and return instance of that list.
So why does this make a difference? Because generic type information is retained in just a couple of places: method and field declarations, and super type declarations. So whereas "raw" List does NOT include any runtime type information, class definition of "MyStringList" does, through its supertype declarations.
Note that assignments to seemingly typed variables do not help: it just creates more compile-time syntactic sugar: real type information is only passed with Class instances (or lib-provided extensions thereof, like JavaType and TypeReference in Jackson's case).
Other than this, you would need to figure out how to pass Jackson either JavaType or TypeReference to accompany value.
If I read the docs for MappingJacksonHttpMessageConverter right, you will have to create and register a subclass of MappingJacksonHttpMessageConverter and override the getJavaType(Class<?>) method:
Returns the Jackson JavaType for the
specific class. Default implementation
returns
TypeFactory.type(java.lang.reflect.Type),
but this can be overridden in
subclasses, to allow for custom
generic collection handling. For
instance:
protected JavaType getJavaType(Class<?> clazz) {
if (List.class.isAssignableFrom(clazz)) {
return TypeFactory.collectionType(ArrayList.class, MyBean.class);
} else {
return super.getJavaType(clazz);
}
}
I have solved this problem by using the following configuration:
private static final String POJO_ARRAY_LIST = PojoArrayList.class.getCanonicalName();
#Bean
public HttpMessageConverter<Object> httpMessageConverter() {
HttpMessageConverter<Object> httpMessageConverter = new MappingJackson2HttpMessageConverter() {
#Override
protected JavaType getJavaType(Type type, #Nullable Class<?> contextClass) {
JavaType javaType;
if (type != null && POJO_ARRAY_LIST.equals(type.getTypeName())) {
ObjectMapper objectMapper = new ObjectMapper();
TypeFactory typeFactory = objectMapper.getTypeFactory();
CollectionType collectionType = typeFactory.constructCollectionType(ArrayList.class, Pojo.class);
javaType = collectionType;
} else {
javaType = super.getJavaType(type, contextClass);
}
return javaType;
}
};
return httpMessageConverter;
}
where PojoArrayList is a final class that extends ArrayList<Pojo>.
I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info