【问题标题】:Deserialize Generic class Jackson or Gson反序列化通用类 Jackson 或 Gson
【发布时间】:2014-02-27 17:17:57
【问题描述】:

在 .NET 领域,我有一个像这样定义的泛型类..

public class SyncWrapper<T, I>
{
    public IList<T> Data { get; set; }
    public IList<I> DeleteIds { get; set; }
    public DateTime LastSyncDateTime { get; set; }
}

我可以通过简单地调用...从 json 创建这个对象的一个​​实例

JsonConvert.DeserializeObject<SyncWrapper<T, Guid>>(json);

现在我的任务是将此代码移植到 Java/Android。以前从未接触过 Java,我还有很多东西要学!

无论如何,到目前为止,我已经尝试过 Gson 和 Jackson 从 json 获取对象,但没有任何乐趣。我认为我将无法使用涉及的&lt;T&gt;gson.fromJson(json, SyncWrapper&lt;T, UUID&gt;.class) 调用andthing,因为类型Erasure 有问题!

到目前为止,我的努力看起来是这样的......

Gson

Gson gson = new Gson();

SyncWrapper<MyClass, UUID> result = gson.fromJson(json, new TypeToken<SyncWrapper<MyClass, UUID>>() { }.getType());

这会编译,但结果是一个空的 SyncWrapper

杰克逊

ObjectMapper mapper = new ObjectMapper();

SyncWrapper<MyClass, UUID> result = mapper.readValue(json, new TypeReference<SyncWrapper<MyClass, UUID>>() { });

这会编译,但在执行时会导致应用崩溃!!!

我的 Java 版本的 SyncWrapper....

public class SyncWrapper<T, I> {

    private DateTime lastSyncDateTime;
    private Collection<T> data;
    private Collection<I> deleteIds;

    public Collection<T> getData() {
        return data;
    }

    public void setData(Collection<T> data) {
        this.data = data;
    }

    public Collection<I> getDeleteIds() {
        return deleteIds;
    }

    public void setDeleteIds(Collection<I> deleteIds) {
        this.deleteIds = deleteIds;
    }

    public DateTime getLastSyncDateTime() {
        return lastSyncDateTime;
    }

    public void setLastSyncDateTime(DateTime lastSyncDateTime) {
        this.lastSyncDateTime = lastSyncDateTime;
    }
}

我真的被强大的力量深深地投入了(所有编程都是一样的,不是吗?),所以非常感谢任何帮助。

我并不在乎我使用哪个库(Gson、Jackson 等)

更新

要反序列化的Json示例...

{
  "Data": [
    {
      "Name": "Company A",
      "Id": "7d5d236c-c2b5-42dc-aea5-99e6752c8a52"
    },
    {
      "Name": "Company B",
      "Id": "44444444-0000-0000-0000-444444444444"
    },
    {
      "Name": "Company C",
      "Id": "249a4558-05c6-483f-9835-0056804791c9"
    }
  ],
  "DeleteIds": [
    "5f7873a6-b2ee-4566-9714-1577b81384f4",
    "1f224a39-16c3-441d-99de-8e58fa8f31c2"
  ],
  "LastSyncDateTime": "\/Date(1393580073773+0000)\/"
}

..或者这个(DeleteIds 通常为空)...

{
  "Data": [
    {
      "Name": "Company A",
      "Id": "7d5d236c-c2b5-42dc-aea5-99e6752c8a52"
    },
    {
      "Name": "Company B",
      "Id": "44444444-0000-0000-0000-444444444444"
    },
    {
      "Name": "Company C",
      "Id": "249a4558-05c6-483f-9835-0056804791c9"
    }
  ],
  "DeleteIds": null,
  "LastSyncDateTime": "\/Date(1393580073773+0000)\/"
}

对于上面的 json,我将映射到一个 SyncWrapper,其中 T 是 Company...

public class Company extends ModelBase {

    private String name;

    public Company(UUID id, String name) {
        super(id);
        setName(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

【问题讨论】:

  • 没有看到您的 JSON 样本,这是无法回答的。您展示的 Gson 和 Jackson sn-ps 非常好......但似乎 JSON 不匹配(我猜测的一个问题是尝试使用 UUID 类)
  • @BrianRoach - 感谢您的评论,我已经更新了问题以显示一些 json 和一个要 编辑的示例类。您认为可能是 UUID 的什么问题?

标签: java json jackson gson


【解决方案1】:

问题如下:

Java 类中的字段名称与 JSON 中的字段名称不匹配;资本化问题。这就是为什么你在解析后什么都没有得到的原因。

我将使用 Gson 示例只是因为我知道这一点在我脑海中。你可以在杰克逊做同样的事情,但我需要查一下:

public class SyncWrapper<T, I> {

    @SearializedName("LastSyncDateTime")
    private DateTime lastSyncDateTime;
    @SearializedName("Data")
    private Collection<T> data;
    @SearializedName("DeleteIds")
    private Collection<I> deleteIds;

这告诉 Gson Java 中的哪些字段映射到 JSON 中的字段。您也可以使用字段命名策略,因为看起来您的所有字段都是大写的:

Gson g = new GsonBuilder()
             .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
             .build();

现在您的字段将匹配。下一个问题将是UUID 类。 Java 中的那个类不是字符串;它是一个生成 UUID 的类。只需将String 用于Java 类中包含它的类型。

DateTime 类...同样的问题。最重要的是,您的 JSON 中有一些奇怪的日期值。您要么也希望将其存储为 String,要么您将不得不编写一个自定义反序列化器来处理它。

有了这些变化,我认为你可以开始了。

编辑以从 cmets 添加:如果您确实需要 Java UUID 类而不仅仅是 String 表示,您可以编写一段代码来为您处理这个问题:

class UUIDDeserializer implements JsonDeserializer<UUID>
{
    @Override
    public UUID deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
    {
        return UUID.fromString(je.getAsString());
    }
}

然后你可以用 Gson 注册它:

Gson g = new GsonBuilder()
             .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
             .registerTypeAdapter(UUID.class, new UUIDDeserializer())
             .build();

这将使用 UUID 实例填充类中的 UUID 类型字段。这与你需要对那个时髦的日期值做同样的事情。

【讨论】:

  • 像UUID这样的类型可以用TypeAdapter处理
  • @McDowell 当然,如果有必要这样做的话。与UUID ...我不知道有。这实际上取决于最终用例。大多数情况下,无论如何您都将使用 UUID 的字符串表示形式。
  • 虽然我最终选择了 Jackson 库,但问题的主要原因是外壳。正如您所说@BrianRoach“大写很重要”。非常感谢您的帮助。
【解决方案2】:

我建议为此使用 Jackson;它有一个更清晰的 API,不需要像 Gson 那样创建一个新类型(你必须扩展一个类才能做到这一点)。

例子:

public static <T> T fromJsonToGenericPojo(
        String json, Class<?> classType, Class<?>... genericTypes) {

    JavaType javaType = TypeFactory.defaultInstance()
            .constructParametricType(classType, genericTypes);

    try {
        return OBJECT_MAPPER.readValue(json, javaType);
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        throw new IllegalArgumentException(e);
    }
}

【讨论】:

  • 抱歉这个新手问题:你会用以下语法调用它吗 SyncWrapper results = MyUtils.>fromJsonToGeneric(json, SyncWrapper.class, Company .class,UUID.class); ?
  • 是的;虽然我认为编译器应该能够推断出类型(至少从 Java 7)而不是调用MyUtils.&lt;SyncWrapper&lt;Company , UUID&gt;&gt;fromJsonToGeneric(),而是直接调用MyUtils.fromJsonToGeneric()。再说一次,我想,我没有验证。
  • 嗯,不开心。我认为它因为 UUID 而爆炸了,但没有得到异常返回 - 这令人担忧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-10
  • 1970-01-01
  • 1970-01-01
  • 2012-07-24
  • 1970-01-01
  • 2016-07-26
相关资源
最近更新 更多