Gson:是否有更有效的方法来对详细的Json对象进行自定义(反)序列化

时间:2018-03-23 19:01:03

标签: json gson

我正在处理一些我称之为详细的json对象:

{
  "user": {
    "name": "username",
    "email": "blah@blah.com",
    "time_zone": "America/New_York"
  }
}

但我更喜欢用Java POJO来处理它们,如:

class UserDetails {
    String name;
    String email;
    String timeZone;
    ...
}

请注意,我无法控制POJO,因为它是生成的代码。

我对(反)序列化的两个要求是

  • timeZone字段映射到JSON中的time_zone
  • 忽略外部user

所以我有一些客户(de)序列化器:

    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;

        }
    }

我觉得在(de)序列化器逻辑中创建新的Gson对象只是添加和删除最外面的user密钥并不理想/高效。

编辑:实际上.setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)在反序列化方面做得很好。

1 个答案:

答案 0 :(得分:1)

我并不认为这一般是一个好主意,你应该最好为所有人提供一个Gson&#34;最顶层的&#34;目的(如果你不想要你的内心对象被考虑​​&#34;冗长&#34;)。 但是,当你说

时,你就是对的
  

我觉得在(de)序列化器逻辑中创建新的Gson对象只是为了添加和删除最外面的用户密钥而不理想/高效。

所以:

  • 创建Gson是一项相对昂贵的操作。
  • 这只会创建不必要的对象并命中堆。
  • Gson可能会以特殊方式配置,您可能希望在任何地方共享相同的JsonSerializer配置。
  • JsonDeserializerJsonElement对JSON树(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; } } } 及其子类)进行操作,因此它会在序列化/反序列化之前/之后创建一个中间内存树表示。

你可能会考虑一个更快的解决方案,它没有这些项目。

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);

因此,例如,以下代码

username
blah@blah.com
America/New_York
{"user":{"name":"username","email":"blah@blah.com","time_zone":"America/New_York"}}

产生

Gson

结论:

  • 不再过度Gson实例化。
  • 原始FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES实例配置已继承(即JsonElement设置一次)。
  • 没有中间UPDATE (MATCH...RETURN...) SET... 个实例。