I have following class defined:
public class ClassObjectTwo {
Long a;
Long b;
}
public class ClassObjectThree{
Long c;
Long d;
}
public class ClassObject{
private final ClassObjectTwo obj1;
private final ClassObjectTwo obj2;
private final ClassObjectTwo obj3;
private final Multimap<String, ClassObjectThree> obj4;
}
ClassObject classObj;
Gson gson = new Gson();
String jsonString = gson.toJson(classObj);
return jsonString;
JsonString is not returning anything, How to define Object of object to convert into JSON String ?
How to use InstanceCreator here ?
Not entirely sure, what the issue here is, when you say, it's not returning anything. Try making use of GsonBuilder.
you could use - GsonBuilder, and enable the complexMapKeySerialization as below.
Gson gson = new GsonBuilder().registerTypeAdapter(Instant.class, new InstantConverter()).enableComplexMapKeySerialization()
.setPrettyPrinting().create();
Since the Gson uses, toString() methods, for forming the JSON, make sure to use them.
public class ClassObject{
private final ClassObjectTwo obj1;
private final ClassObjectTwo obj2;
private final ClassObjectTwo obj3;
private final Multimap<String, ClassObjectThree> obj4;
#Override
public String toString() {
return new ToStringBuilder(this)
.append("obj1", obj1)
.append("obj2", obj2)
.append("obj3", obj3)
.append("obj4", obj4)
.toString();
}
}
Related
I am working with some json objects that I call verbose:
{
"user": {
"name": "username",
"email": "blah#blah.com",
"time_zone": "America/New_York"
}
}
But I'd prefer to just deal with them in terms of java POJOs like:
class UserDetails {
String name;
String email;
String timeZone;
...
}
Note that I have no control over the POJO as it is generated code.
My two requirements for (de)serialization is that
the timeZone field maps to time_zone in JSON
the outer user is ignored
So I have some customer (de)serializers:
class UserDeserializer implements JsonDeserializer<UserDetails> {
#Override
public UserDetails deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonElement content = je.getAsJsonObject().get("user");
UserDetails userDetails = new GsonBuilder()
.setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
.fromJson(content, UserDetails.class);
return userDetails;
}
}
class UserSerializer implements JsonSerializer<UserDetails> {
#Override
public JsonElement serialize(UserDetails userDetails, Type typeOfSrc,
JsonSerializationContext context) {
JsonObject obj = new JsonObject();
JsonElement je = new GsonBuilder()
.setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create().toJsonTree(userDetails);
obj.add("user", je);
return obj;
}
}
I feel like creating new Gson objects in the (de)serializer logic is not ideal/efficient just to add and remove the outermost user key.
EDIT: Actually .setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) does work fine on deserialization.
I don't really think it's a good idea in general, and you should probably better have a single Wrapper<T> for all "top-most" purposes (if you don't want your inner objects to be considered "verbose").
But you're right when you say
I feel like creating new Gson objects in the (de)serializer logic is not ideal/efficient just to add and remove the outermost user key.
So:
Creating a Gson is a relatively expensive operation.
This just creates unnecessary objects and hits the heap.
Gson may be configured in a special way and you might want to share the same Gson configuration everywhere.
JsonSerializer and JsonDeserializer operate on JSON trees (JsonElement and its subclasses), therefore it creates an intermediate in-memory tree representations before/after serialization/deserialization.
You might consider a faster solution, that's free of those items.
final class VerboseTypeAdapterFactory
implements TypeAdapterFactory {
private final Map<Class<?>, String> mappings;
private VerboseTypeAdapterFactory(final Map<Class<?>, String> mappings) {
this.mappings = mappings;
}
static TypeAdapterFactory get(final Map<Class<?>, String> mappings) {
// Create a defensive copy to make sure the map is not modified from outside
final Map<Class<?>, String> mappingsCopy = mappings
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return new VerboseTypeAdapterFactory(mappingsCopy);
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final Class<? super T> rawType = typeToken.getRawType();
// Not something we can handle?
if ( !mappings.containsKey(rawType) ) {
// Then let Gson do its job elsewhere
return null;
}
// Getting a property name we want to use for a particular class
final String propertyName = mappings.get(rawType);
// And getting the original type adapter for this class (effectively ReflectiveTypeAdapterFactory.Adapter)
final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
return VerboseTypeAdapter.get(propertyName, delegateTypeAdapter);
}
private static final class VerboseTypeAdapter<T>
extends TypeAdapter<T> {
private final String propertyName;
private final TypeAdapter<T> delegateTypeAdapter;
private VerboseTypeAdapter(final String propertyName, final TypeAdapter<T> delegateTypeAdapter) {
this.propertyName = propertyName;
this.delegateTypeAdapter = delegateTypeAdapter;
}
private static <T> TypeAdapter<T> get(final String propertyName, final TypeAdapter<T> delegateTypeAdapter) {
return new VerboseTypeAdapter<>(propertyName, delegateTypeAdapter)
// A convenient method to simplify null-handling
.nullSafe();
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final T object)
throws IOException {
// Open the object with `{`
out.beginObject();
// Prepend the object with its reserved name
out.name(propertyName);
// Write the object
delegateTypeAdapter.write(out, object);
// And close the object with `}`
out.endObject();
}
#Override
public T read(final JsonReader in)
throws IOException {
// Assume the very first token is `{`
in.beginObject();
// Peeking what's the actual property name
final String actualPropertyName = in.nextName();
// And if it's not we expect, throw a JSON parse exception
if ( !actualPropertyName.equals(propertyName) ) {
throw new JsonParseException("Expected " + propertyName + " but was " + actualPropertyName);
}
// Otherwise read the value led by the property name
final T object = delegateTypeAdapter.read(in);
// And make sure there are no more properties
if ( in.hasNext() ) {
throw new JsonParseException(propertyName + " is expected to be the only top-most property");
}
// Assume the very last token is `}` (this works for the check above, but we made it more semantical)
in.endObject();
return object;
}
}
}
So, for example, the following code
private static final Gson gson = new GsonBuilder()
.setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapterFactory(VerboseTypeAdapterFactory.get(ImmutableMap.of(UserDetails.class, "user")))
.create();
...
final UserDetails userDetails = gson.fromJson(jsonReader, UserDetails.class);
System.out.println(userDetails.name);
System.out.println(userDetails.email);
System.out.println(userDetails.timeZone);
final String json = gson.toJson(userDetails);
System.out.println(json);
produces
username
blah#blah.com
America/New_York
{"user":{"name":"username","email":"blah#blah.com","time_zone":"America/New_York"}}
As the conclusion:
No more excessive Gson instantiation.
Original Gson instance configuration inherited (i.e. FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES set once).
No intermediate JsonElement instances.
in my JSON string its contain ",
{"value":" "Testing""}
If did not perform any convertion or replace of string, the default retrofit's parser doesn't take care of the escape characters, and i will have " in my result, so I found code here that help me to remove the escape characters before the retrofit's parse process, here is the modification code
public class MyJsonConverter extends Converter.Factory {
public static MyJsonConverter create() {
return create(new Gson());
}
public static MyJsonConverter create(Gson gson) {
return new MyJsonConverter(gson);
}
private final Gson gson;
private MyJsonConverter(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
#Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
#Override
public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close();
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
#Override
public T convert(ResponseBody value) throws IOException {
String dirty = value.string();
String clean = dirty.replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" +
"<string xmlns=\"http://tempuri.org/\">","").replace("</string>","");
clean = clean
.replace(""", "\"")\\This line!!!
.replace("\\/", "/") ;
try {
return adapter.fromJson(clean);
} finally {
value.close();
}
}
}
}
when I execute this code, it will fail later in retrofit parsing code and throw me this error
com.google.gson.stream.MalformedJsonException: Unterminated object at
line 1 column 1121 path
$.entry_list[0].name_value_list.highlight1_c.value
I understand the meaning of this error, but what is the right way to do this? And I doesn't like to replace escape characters after the retrofit Parse the Json into POJO, because that will be a lot of messy replace everywhere. Thanks for any advice.
I make a mistake on this line of code, it should escape it with a backslash: before ".
.replace(""", "\\\"")
I call restTemplate:
restTemplate.exchange(uri, HttpMethod.GET, prepareHttpEntity(), MyDeserializedClass.class);
MyDeserializedClass:
public class MyDeserializedClass {
private final String id;
private final String title;
#JsonCreator
public MyDeserializedClass(#JsonProperty("id") String id,
#JsonProperty("title") String title) {
this.pageId = pageId;
this.title = title;
}
}
When there is no object inside json I'm getting MyDeserializedClass with null values.
I've tried to annotate MyDeserializedClass with
#JsonInclude(JsonInclude.Include.NON_NULL) or #JsonIgnoreProperties(ignoreUnknown = true) but with no luck.
Is there any way to retrieve another object (or some kind of callback) in such situation?
You can use static function as your main #JsonCreator instead of constructor
public class MyDeserializedClass {
private final String id;
private final String title;
public MyDeserializedClass () {}
#JsonCreator
public static MyDeserializedClass JsonCreator(#JsonProperty("id") String id, #JsonProperty("title") String title){
if (id == null || title == null) return null;
//or some other code can go here
MyDeserializedClass myclass = new MyDeserializedClass();
myclass.id = id; // or use setters
myclass.title = title;
return myclass;
}
}
This way you can return null or some sort of MyDeserializedClass subclass instead of MyDeserializedClass with null values
You could try to deserialize object by yourself i.e.:
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, prepareHttpEntity(), String.class);
try {
MyDeserializedClass myClass = new ObjectMapper().readValue(response.body, MyDeserialized.class);
return ResponseEntity.ok(myClass);
} catch(IOException e) {
//log exception
return ResponseEntity.notFound().build();
}
When I use GSON deserialize a JSON String, it throws JsonSyntaxException. How can I deal with it?
{"QueueNum":1,"Result":"2","ResultMessage":"201","ReturnValue":"","TS":1448443938}
public class Response<T> {
private int Result;
private long TS;
private float ResultMessage;
private int QueueNum;
private T ReturnValue;
}
I have a situation where the gson is returning the child object elements also when i tried to make a json string from parent object. how to eliminate the same.
Here is the code i am having.
Class Image {
private int imageID;
private String imageName;
// Getters and setters
}
Class ImageDetails extends Image {
private String imageType;
private byte[] imageData;
//Getters and setters
}
Class Test {
// Setting the image Object, and the imageDetails.
// calling the gson for json string
String jsonString = GsonString.UserFeed(ImageObject)
// This jsonString has all the elements from the ImageDetails Object also which i do not want.
}
Class GsonString {
public static String UserFeed(Object feedData) {
String feeds = null;
Gson gson = new Gson();
feeds = gson.toJson(feedData);
return feeds;
}
}
You only have to specify the class you want to serialize, using toJson(Object src,Type typeOfSrc)
A simple example:
class Bob {
private String bobName = "Bob";
}
class Pete extends Bob {
private String peteName = "Bob";
}
public static void main(String[] args) {
Object o = new Pete();
System.out.println(new Gson().toJson(o));
System.out.println(new Gson().toJson(o, Bob.class));
}
Output:
{"peteName":"Bob","bobName":"Bob"}
{"bobName":"Bob"}