首页 > 解决方案 > 使用 Gson 根据嵌套 JSON 对象的父值填充空值

问题描述

我正在反序列化一个 JSON 数据结构,如下面的 GSON 示例:

{
  "id": 1,
  "data": [
    {
      "value": 2
    },
    {
      "value": 2
    }
  ]
}

此 JSON 映射到以下类:

class Element {
   Integer id;
   List<SubElement> data;
   ...
}

class SubElement {
   Integer id;
   Integer value;
   ...
}

我希望能够在反序列化时使用 GSON填充基于父字段id的对象字段。SubElementid

这样做的目的是我在我的应用程序的几个部分收到这种 JSON,例如我想省略这种代码:

child.setId(parent.getId());

我尝试实现自定义反序列化器JsonDeserializer<SubElement>,但我无权访问根元素,因此无法访问id.


有没有办法进行这种类型的自定义反序列化?或者我应该继续实施某种解决方案,Reflection例如遍历整个对象以寻找感兴趣的领域并相应地更新它?

我正在寻找一种通用解决方案,它可能对其他缺少id.

标签: javajsongson

解决方案


有没有办法进行这种类型的自定义反序列化?

是的,但它不一定需要成为 Gson 反序列化的一部分,除非您希望将其集中化。您总是可以在调用其fromJson方法后调整对象(因此不那么可靠)。因此,如果您认为 Gson 应该对此负责,您可以将其实现为后处理器:

public interface IPostProcessorResolver<T> {

    @Nullable
    Consumer<T> resolvePostProcessor(@Nonnull TypeToken<?> typeToken);

}
public final class PostProcessingTypeAdapterFactory
        implements TypeAdapterFactory {

    private final IPostProcessorResolver<?> resolvePostProcessor;

    private PostProcessingTypeAdapterFactory(final IPostProcessorResolver<?> resolvePostProcessor) {
        this.resolvePostProcessor = resolvePostProcessor;
    }

    public static TypeAdapterFactory create(final IPostProcessorResolver<?> resolvePostProcessor) {
        return new PostProcessingTypeAdapterFactory(resolvePostProcessor);
    }

    @Override
    @Nullable
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        @Nullable
        @SuppressWarnings("unchecked")
        final Consumer<T> postProcessor = (Consumer<T>) resolvePostProcessor.resolvePostProcessor(typeToken);
        if ( postProcessor == null ) {
            return null;
        }
        final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter out, final T value)
                    throws IOException {
                delegateTypeAdapter.write(out, value);
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                // First, read the object
                final T object = delegateTypeAdapter.read(in);
                // Second, tell it to adjust itself
                postProcessor.accept(object);
                return object;
            }
        };
    }

}

然后,一个测试:

final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(PostProcessingTypeAdapterFactory.create((IPostProcessorResolver<Element>) typeToken -> {
            if ( typeToken.getRawType() != Element.class ) {
                return null;
            }
            return element -> {
                if ( element.data == null ) {
                    return;
                }
                for ( @Nullable final SubElement datum : element.data ) {
                    if ( datum != null ) {
                        datum.id = element.id;
                    }
                }
            };
        }))
        .create();
...
final Element element = gson.fromJson(jsonReader, Element.class);
Assertions.assertNotNull(element.data);
Assertions.assertEquals(1, (int) element.id);
Assertions.assertEquals(1, (int) element.data.get(0).id);
Assertions.assertEquals(1, (int) element.data.get(1).id);

请注意,您可以通过仅实现IPostProcessorResolver: multi types support with type-resolving 来实现更复杂的策略;反射使用;等等


推荐阅读