使用自定义反序列化器 Jackson 将 .json 文件转换为 Java 对象

Converting a .json file into a Java object with a custom deserializer Jackson

在我的项目中,我有一个名为 Closet 的 class,它包含一个名为 clothes 的服装列表。我已经实现了将 Closet 对象的信息保存到 .json 文件中的代码,结果如下所示:

{
  "clothes" : [ {
    "name" : "mypants",
    "type" : "pants",
    "color" : "red",
    "size" : 32.0,
    "needsWashing" : false
  }, {
    "name" : "myshirt",
    "type" : "shirt",
    "color" : "blue",
    "size" : 2.0,
    "needsWashing" : false
  } ],
  "numberOfClothing" : 2
}

但是,当我尝试检索 Json 文件并将其转换回壁橱时,抛出了 JsonProcessingException。

Closet closet = getDefaultObjectMapper().readValue(Paths.get("./data/Closet.json")
                .toFile(), Closet.class);

我是初学者 java 程序员,我不确定如何解决这个问题。我已经通过创建自定义反序列化器进行了一些搜索,但是,我不确定如何使用我的嵌套对象(Clothing 嵌套在 Closet 中)以及 Closet 对象中可能有任意数量的 Clothing 来实现反序列化器。

我对另一个名为 StyleBoard 的 class 有同样的问题,它是一个 Outfit 列表(另一个 class),而一个 Outfit 是一个 Clothing 列表。样式板的 json 文件写入 .json 文件的示例是

contains 2 outfits "shirt and pants" and "pants and socks" each with 2 Clothing objects

{
  "styleBoard" : [ {
    "clothes" : [ {
      "name" : "mypants",
      "type" : "pants",
      "color" : "blue",
      "size" : 32.0,
      "needsWashing" : false
    }, {
      "name" : "myshirt",
      "type" : "shirt",
      "color" : "blue",
      "size" : 0.0,
      "needsWashing" : false
    } ],
    "favorite" : false,
    "name" : "shirt and pants",
    "numberOfClothing" : 2
  }, {
    "clothes" : [ {
      "name" : "mypants",
      "type" : "pants",
      "color" : "blue",
      "size" : 32.0,
      "needsWashing" : false
    }, {
      "name" : "mysocks",
      "type" : "socks",
      "color" : "white",
      "size" : 3.0,
      "needsWashing" : false
    } ],
    "favorite" : false,
    "name" : "pants and socks",
    "numberOfClothing" : 2
  } ],
  "numberOfOutfits" : 2
}

这是我收到的错误消息:

Exception in thread "main" java.lang.RuntimeException: Cannot construct instance of `model.Clothing` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{
  "clothes" : [ {
    "name" : "myshirt",
    "type" : "shirt",
    "color" : "black",
    "size" : 2.09,
    "needsWashing" : false
  }, {
    "name" : "mypants",
    "type" : "pants",
    "color" : "red",
    "size" : 32.0,
    "needsWashing" : false
  } ],
  "collectionSize" : 2
}"; line: 3, column: 5] (through reference chain: model.Closet["clothes"]->java.util.ArrayList[0])
    at persistence.Json.fromJson(Json.java:43)
    at persistence.Json.parseUserCloset(Json.java:104)
    at ui.ClosetApp.loadUser(ClosetApp.java:195)
    at ui.ClosetApp.runClosetApp(ClosetApp.java:173)
    at ui.ClosetApp.doLogin(ClosetApp.java:103)
    at ui.ClosetApp.processLoginCommand(ClosetApp.java:68)
    at ui.ClosetApp.runLogin(ClosetApp.java:49)
    at ui.ClosetApp.<init>(ClosetApp.java:26)
    at ui.Main.main(Main.java:5)

我注意到的一个可能导致此问题的问题是,当我将壁橱对象写入 .json 文件时,它添加了一个额外的字段 "numberOfClothing" 如你所见。这不是我的 Closet 构造函数的一部分,但是我确实有一个名为 getNumberOfClothing 的 getter。当我将此 getter 的名称更改为 getCollectionSize 时,添加到我的 json 的这个额外字段变为 "collectionSize"为什么要创建这个额外的字段?我怎样才能防止它? 我认为这个额外的字段导致从 json.

重建对象时出现问题

首先,您需要根据您的 json 创建 classes。 您需要创建一个壁橱 class.

仅供参考,我使用 Lombok 是为了简洁,即不需要 getter、setter 和 toString 方法。如果您不使用 Lombok,则必须创建 getter 和 setter,因为 Jackson 库将使用此 getter 和 setter。

@Getter
@Setter
@ToString
@FieldDefaults(level= AccessLevel.PRIVATE)
public class Closet {
    // you need to create getter and setter method.
    // i use lombok so I don't need to create one
    List<Cloth> clothes = new ArrayList<>();
    Integer numberOfClothing;
}

然后是布料class:

@Getter
@Setter
@ToString
@FieldDefaults(level= AccessLevel.PRIVATE)
public class Cloth {
    String name;
    String type;
    String color;
    Float size;
    Boolean needsWashing;
}

如果您需要命名您的 class 成员而不是 json 字段,您可以使用 @JsonProperty("json 字段名称") 进行注释,例如

@JsonProperty("name")
String clothName;

那么实际的转换代码:

Closet closet = JsonUtil.fromJson(jsonStr, Closet.class);

其中 JsonUtil class 包含此静态方法(使用 jackson):

public static <T> T fromJson(String jsonStr, Class<T> clazz){
    try {
        return new ObjectMapper().readValue(jsonStr, clazz);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
    }
}

您可以通过下面的 maven 依赖获取 jackson 库:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.0</version>
</dependency>

jackson教程,可以google。下面只是其中一个例子: https://www.baeldung.com/jackson-object-mapper-tutorial

我把样式板留给你,因为步骤与壁橱完全相同 class。

问题出在我的 Clothing class 没有带空参数的构造函数。我只是添加了另一个构造函数。

额外的无法识别的字段“numberOfclothing”的问题是由于我的 Closet 和 StyleBoard 类 都对不属于字段的值但对使用列表类型。我只需要更改这些方法的名称,使其名称中不包含“get”即可。