I am using the Gson parser in Java 7. I would like to preserve one of my properties as a raw JSON string so I can deserialize it into the desired class later.
Example input JSON:
{
"foo":"bar",
"body":["blah"]
}
or
{
"foo":"bar",
"body":{"a":"b"}
}
Deserializes to a class like this:
public class Message {
public String foo;
public String bar; //I want this to be a raw JSON string, not parsed
}
Code:
String input = "{\"foo\":\"bar\", \"body\":[\"blah\"]}";
Message message = gson.fromJson(input, Message.class);
message.foo; //"bar"
message.body; //"[\"blah\"]"
Is this possible?
UPDATE:
I'm currently doing this, but it would be nice if it could be "cleaner" and use the native String
type somehow...
public class Message {
public String foo;
public JsonString body;
}
public static class JsonString {
public String body;
}
public static class JsonStringDeserializer implements JsonDeserializer<JsonString> {
@Override
public JsonString deserialize(JsonElement json, Type type, JsonDeserializationContext context) {
JsonString a = new JsonString();
a.body = json.toString();
return a;
}
}
The thing I don't like about this is that I have to use the deserialized object like so:
Message message = gson.fromJson(input, Message.class);
//notice how i have to use the inner string
SomeClass cls = gson.fromJson(message.body.body, SomeClass.class);
If you don't mind declaring your body
property as an Object
instead of a String
to get around JSON decoding problems; you can do the following.
Annotation definition for JSONRawString
:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JSONRawString {
}
The Message
class:
public class Message {
public String foo;
@JSONRawString
public Object body;
}
The deserializer:
public class JSONRawStringDeserializer<T> implements JsonDeserializer<T> {
@Override
public T deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
//We decode using the normal deserializer; but will run over all the fields and check if our annotation exists. This is a bit hacky; but should work if you don't mind declaring your objects which you'd like to maintain as deserialized as Object.
T serializedObject = new Gson().fromJson(jsonElement, type);
Field[] fields = serializedObject.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(JSONRawString.class) != null) {
try {
String fieldName = field.getName();
if(field.getAnnotation(SerializedName.class) != null) {
fieldName = field.getAnnotation(SerializedName.class).value();
}
String element = new Gson().toJson(jsonElement.getAsJsonObject().get(fieldName).toString());
field.set(serializedObject, element);
} catch(IllegalAccessException e) {
throw new JsonParseException(e);
}
}
}
return serializedObject;
}
}
Finally in Main
:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Main {
public static void main(String[] args) {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Message.class, new JSONRawStringDeserializer<Message>())
.create();
String input = "{\"foo\":\"bar\", \"body\":[\"blah\"]}";
Message message = gson.fromJson(input, Message.class);
System.out.printf("message.foo: %s, message.body: %s", message.foo, message.body);
}
}
Resulting in message.foo: bar, message.body: "[\"blah\"]"
This is sort of hacky; because we override what Gson would have spit out with our own method; and I don't think this works recursively. Hopefully it will lead you to a solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With