I would like to fill object fields with values from Json in object constructor.
public AbstractObject(String jsonSerializedObject) {
Gson gson = new Gson();
// Fill object values from json in *current* object
}
Is this possible?
Gson doesn't support "populating existing object" (yet). See issue 431 here.
http://code.google.com/p/google-gson/issues/detail?id=431&q=existing
You can use an InstanceCreator to supply the object that you want to populate.
final Foo existing;
InstanceCreator<Foo> creator = new InstanceCreator<Foo>() {
public Foo createInstance(Type type) { return existing; }
}
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, creator).create();
Foo value = gson.fromJson(jsonString, Foo.class);
// value should be same as existing
http://www.javacreed.com/gson-typeadapter-example/
Override the constructor with the object you want to fill, and then update the object in the read(jsonReader: JsonReader) method.
Example in Kotlin:
class TypeAdapterGen : TypeAdapter<Gen> {
var mGen: Gen
constructor(gen: Gen) : super(){
mGen = gen
}
}
Related
I currently have a custom serialiser. Purpose of my custom serialiser is to format my JSON object from this: {"id":["21","22", 23"]} to this {"id":"21,22,23"}.
My current implementation of my custom serialiser:
public static class ListSerialiser implements JsonSerializer<List<Member>> {
#Override
public JsonElement serialize(List<Member> src, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
List<String> memberId = new ArrayList<>(src.size());
for (Member member : src) {
memberId.add("" + Member.getId());
}
String memberIdAsString = TextUtils.join(",", memberId);
object.addProperty("[%s]", memberIdAsString);
return object;
}
}
Although my implementation got me what I wanted, just out of curiosity, I was wondering if there's a way to serialise without having to use Text Utils or string formatters to achieve this outcome: {"id":"21,22,23"}
There are a lot of ways to join strings, you can see most of them here
My favourite is:
String memberIdAsString = memberId.stream().collect(Collectors.joining(","));
I need only one field to change, another fields i want to have dafault value, but using this code i have only one field in the output - the one i write in JsonSerializer, but i need to have all field and only one for change. There is a method of property for this?
GsonBuilder gson = new GsonBuilder().serializeNulls();
gson.registerTypeAdapter(TripCardView.class, new JsonSerializer<TripCardView>() {
#Override
public JsonElement serialize(TripCardView src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jObj = new JsonObject();
jObj.add("numberShortYear", new JsonPrimitive(src.getNumberShortYear()));
return jObj;
}
});
jsonResponse.add("aaData", gson.setDateFormat("dd.MM.yyyy").create().toJsonTree(result));
Just few little changes, see comments in the code below:
gson.registerTypeAdapter(TripCardView.class, new JsonSerializer<TripCardView>() {
// You need to create a new Gson in your serializer because calling original contex
// would call this serializer again and cause stack overflow because of recursion
private Gson gson = new GsonBuilder().setDateFormat("dd.MM.yyyy").create();
#Override
public JsonElement serialize(TripCardView src, Type typeOfSrc,
JsonSerializationContext context) {
// You need to serialize the original object to have its fields populated 'default'
JsonElement result = gson.toJsonTree(src);
// After that it is just to add the extra field with value from method call
result.getAsJsonObject().add("numberShortYear",
new JsonPrimitive(src.getNumberShortYear()));
return result;
}
});
I was having an issue with Gson because it can't pass to Json HibernateProxy objects, so I have followed this guide: link
This solve the typeAdapter problem with Gson, but now I'm getting the following exception:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
I have been searching how to solve this but the solutions that I have found don't work in this case.
This is my code:
List<ContratoSolicitudFull> returnValue =
new SomeBL().getData(id, null, null,currentUserId);
Type type = new TypeToken<List<ContratoSolicitudFull>>() {}.getType();
Gson gson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, new DateTimeJsonConverter())
.registerTypeAdapter(LocalDate.class, new LocalDateJsonConverter())
.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY)
.create();
String jsonValue = gson.toJson(returnValue, type); //Here is where it fail
Any idea?
By serialization to json, you are accessing unfetched data outside of a session context.
If you used exactly the same code as in your link then, try to change write method to this.
#SuppressWarnings({"rawtypes", "unchecked"})
#Override
public void write(JsonWriter out, HibernateProxy value) throws IOException {
//avoid serializing non initialized proxies
if (value == null || !Hibernate.isInitialized(value)) {
out.nullValue();
return;
}
// Retrieve the original (not proxy) class
Class<?> baseType = Hibernate.getClass(value);
// Get the TypeAdapter of the original class, to delegate the serialization
TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
// Get a filled instance of the original class
Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()
.getImplementation();
// Serialize the value
delegate.write(out, unproxiedValue);
}
|| !Hibernate.isInitialized(value) was added to check if collection was initialized, and if not avoids accessing it.
Is there any way in Gson to map multiple JSON fields to a single Java object member variable?
Let's say I have a Java class...
public class MyClass {
String id;
String name;
}
I want to use this single class with two different services. However, these two services differ in how they return their data...
{ "id": 2341, "person": "Bob" }
... and ...
{ "id": 5382, "user": "Mary" }
... respectively.
Is there any way to map both the "person" and "user" fields in the JSON string to the name field in the Java object?
(Note: I only ever need to convert from JSON string to Java object - never the other way around.)
In October 2015, Gson version 2.4 (changelog) added the ability to use alternate/multiple names for #SerializedName when deserializing. No more custom TypeAdapter needed!
Usage:
java
#SerializedName(value="name", alternate={"person", "user"})
kotlin
#SerializedName(value="name", alternate= ["person", "user"])
https://www.javadoc.io/doc/com.google.code.gson/gson/2.6.2/com/google/gson/annotations/SerializedName.html
for Kotlin fans
#SerializedName(value="name", alternate= ["person", "user"])
It is not supported to define multiple #SerializedName annotations to a field at Gson.
Reason: By default Deserialization is managed with a LinkedHashMap and the keys are defined by incoming json's field names (not the custom class's field names or the serializedNames) and there is a one to one mapping. You can see the implementation(how deserialization works) at ReflectiveTypeAdapterFactory class's inner class Adapter<T>'s read(JsonReader in) method.
Solution:
You can write a custom TypeAdapter which handles name, person and user json tags and maps them to name field of your custom class MyClass:
class MyClassTypeAdapter extends TypeAdapter<MyClass> {
#Override
public MyClass read(final JsonReader in) throws IOException {
final MyClass myClassInstance = new MyClass();
in.beginObject();
while (in.hasNext()) {
String jsonTag = in.nextName();
if ("id".equals(jsonTag)) {
myClassInstance.id = in.nextInt();
} else if ("name".equals(jsonTag)
|| "person".equals(jsonTag)
|| "user".equals(jsonTag)) {
myClassInstance.name = in.nextString();
}
}
in.endObject();
return myClassInstance;
}
#Override
public void write(final JsonWriter out, final MyClass myClassInstance)
throws IOException {
out.beginObject();
out.name("id").value(myClassInstance.id);
out.name("name").value(myClassInstance.name);
out.endObject();
}
}
Test case:
String jsonVal0 = "{\"id\": 5382, \"user\": \"Mary\" }";
String jsonVal1 = "{\"id\": 2341, \"person\": \"Bob\"}";
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyClass.class, new MyClassTypeAdapter());
final Gson gson = gsonBuilder.create();
MyClass myClassInstance0 = gson.fromJson(jsonVal0, MyClass.class);
MyClass myClassInstance1 = gson.fromJson(jsonVal1, MyClass.class);
System.out.println("jsonVal0 :" + gson.toJson(myClassInstance0));
// output: jsonVal0 :{"id":5382,"name":"Mary"}
System.out.println("jsonVal1 :" + gson.toJson(myClassInstance1));
// output: jsonVal1 :{"id":2341,"name":"Bob"}
Examples about TypeAdapters.
Edit 2016.04.06 : As #Mathieu Castets has written at his answer, it is supported now. (That is the correct answer for this question.)
public abstract String[] alternate
Returns: the alternative names of
the field when it is deserialized Default: {}
For KOTLIN i used below but doesn't work
#SerializedName(value="name", alternate= ["person", "user"])
so i edited it and here it works fine!!
#SerializedName(value="name", alternate= arrayOf("person", "user"))
Why this does not work?
public static class MyBean extends HashMap<String, String> {
public String city;
}
/**
* #param args
*/
public static void main(String[] args) {
MyBean bean = new MyBean();
bean.city = "some city";
Gson gson = new Gson();
String json = gson.toJson(bean);
System.out.println(json);
}
Why I dont see city value in json?
That's because instances implementing Map have special treatment by Gson. By default only its entry set will be serialized. You need to create a custom serializer which serializes both the entryset and the bean properties of interest separately.
E.g.
public class MyBeanSerializer implements JsonSerializer<MyBean> {
#Override
public JsonElement serialize(MyBean myBean, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.add("map", context.serialize(new HashMap<String, String>(myBean)));
object.add("city", context.serialize(myBean.city));
return object;
}
}
Which can be used as follows:
Gson gson = new GsonBuilder().registerTypeAdapter(MyBean.class, new MyBeanSerializer()).create();
String json = gson.toJson(bean);
System.out.println(json);
Update: hmm, as per the comment on the question (you should actually have posted it on the answer so that I will be notified immediately), the context.serialize(myBean.entrySet()) didn't seem to work out. And you're right, I just did a local test and I got the same exception. I tried adding a TypeToken on Set<Entry<String, String>>, but that ends up with an empty entryset somehow. Wrapping it in another map did work for me. I've updated the line in the answer.