Prevent GSON from serializing JSON string

5.1k Views Asked by At

I'm new to gson, and have newby question which I have not found an answer to, so please bear with me. StackOverflow and google were not my friend :(

I have a java class "User", and one of its properties, "externalProfile" is a Java String containing already serialized JSON. When gson serializes the User object, it will treat externalProfile as primitive and thus escaping the JSON adding extra slashes etc. I want gson to leave the string alone, just using it "as is", because it is already valid and usable JSON.

To distinguish the JSON string, I created a simple class called JSONString, and I've tried using reader/writers, registerTypeAdapter, but nothing works. Can you help me out?

public class User {
    private JSONString externalProfile;
    public void setExternalProfile(JSONString externalProfile) { this.externalProfile = externalProfile; }

}

public final class JSONString {
    private String simpleString;
    public JSONString(String simpleString) { this.simpleString = simpleString; }
}

public customJsonBuilder(Object object) {
    GsonBuilder builder = new GsonBuilder();
        builder.registerTypeAdapter(GregorianCalendar.class, new JsonSerializer<GregorianCalendar>() {
            public JsonElement serialize(GregorianCalendar src, Type type, JsonSerializationContext context) {
                if (src == null) {
                    return null;
                }
                return new JsonPrimitive(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(src.getTime()));
            }
        });
        Gson gson = builder.create();
        return gson.toJson(object);
}

As en example, the externalProfile will hold (as String value):

{"profile":{"registrationNumber": 11111}}

After I store it as JSONString in the User object, and we convert the user object to JSON:

User user = new User();
user.setExternalProfile(new JSONString(externalProfile)),  
String json = customJsonBuilder(user);

json will hold something like:

{\"profile\":{\"registrationNumber\": 11111}}

So, the externalProfile JSONString is serialized by gson as String primitive, adding the extra slashes in front of the doublequotes. I want gson to leave this JSONString as is, because it already is usable JSON. I'm looking for a type adapter / reader-writer to do this, but I can't get it to work.

3

There are 3 best solutions below

0
On BEST ANSWER

As stated by Alexis C:

store the externalProfile as a JsonObject first:

new Gson().fromJson(externalProfile, JsonObject.class));

And let gson serialize this again when outputting the User object. Will produce exactly the same JSON!

0
On

Add the read method.

public class RawJsonGsonAdapter extends TypeAdapter<String> {

    @Override
    public void write(final JsonWriter out, final String value) throws IOException {
        out.jsonValue(value);
    }

    @Override
    public String read(final JsonReader in) throws IOException {
        var sb = new StringBuilder();
        int n = 0;
        while (true) {    
            switch (in.peek()) {
            case BEGIN_ARRAY:
                in.beginArray();
                sb.append("[");
                break;
            case BEGIN_OBJECT:
                in.beginObject();
                sb.append("{");
                n++;
                break;
            case BOOLEAN:
                sb.append(in.nextBoolean()).append(",");
                break;
            case END_ARRAY:
                dropLastComma(sb);
                in.endArray();
                sb.append("]");
                break;
            case END_DOCUMENT:
                throw new RuntimeException("END_DOCUMENT invalid here");
            case END_OBJECT:
                dropLastComma(sb);
                in.endObject();
                sb.append("}");
                if (--n == 0)
                    return sb.toString();
                break;
            case NAME:
                sb.append("\"").append(in.nextName()).append("\":");
                break;
            case NULL:
                in.nextNull();
                sb.append("");
                break;
            case NUMBER:
                try {
                    sb.append(in.nextInt()).append(",");
                    break;
                } catch (Exception e1) {
                    try {
                        sb.append(in.nextLong()).append(",");
                        break;
                    } catch (Exception e2) {
                        sb.append(in.nextDouble()).append(",");
                        break;
                    }
                }
            case STRING:
                sb.append("\"").append(in.nextString()).append("\",");
                break;
            }
        }
    }

    private void dropLastComma(StringBuilder sb) {
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.setLength(sb.length() - 1);
        }
    }
}
0
On

I solved it without the unnecessary deserialisation-serialization. Create class:

public class RawJsonGsonAdapter extends TypeAdapter<String> {

    @Override
    public void write(final JsonWriter out, final String value) throws IOException {
        out.jsonValue(value);
    }

    @Override
    public String read(final JsonReader in) throws IOException {
        return null; // Not supported
    }
}

And use it by annotation where needed. For example:

public class MyPojo {
    @JsonAdapter(RawJsonGsonAdapter.class)
    public String someJsonInAString;

    public String normalString;
}

That's it. Use Gson as normal.