英文:
Converting a .json file into a Java object with a custom deserializer Jackson
问题
在我的项目中,我有一个名为Closet的类,它包含一个名为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文件并将其转换回Closet对象时,会抛出JsonProcessingException异常:
Closet closet = getDefaultObjectMapper().readValue(Paths.get("./data/Closet.json").toFile(), Closet.class);
我是一个初学者的Java程序员,不确定如何解决这个问题。我已经尝试创建自定义反序列化器,但是不确定如何在嵌套对象(Closet中嵌套了Clothing对象)以及Closet对象中可能包含任意数量的Clothing的情况下实现反序列化器。
我在另一个名为StyleBoard的类中也遇到了类似的问题,它是Outfit(另一个类)的列表,而Outfit则是Clothing的列表。StyleBoard写入.json文件的示例如下所示:
{
"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)
我注意到可能引起此问题的一个问题是,当我将Closet对象写入.json文件时,它会添加一个额外的字段"numberOfClothing",如上所示。这不是我的Closet构造函数的一部分,但是我有一个名为getNumberOfClothing的getter。当我将此getter的名称更改为getCollectionSize时,添加到我的json中的这个额外字段变成了"collectionSize"。为什么会创建这个额外字段?我如何防止它?我认为这个额外字段正在导致从json重构对象时出现问题。
英文:
In my project, I have a class called Closet which holds a list of clothing called clothes. I have implemented the code to saved the information of Closet objects into a .json file which turns out like this:
{
"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
}
However, a JsonProcessingException is thrown when I try to retrieve the Json file and convert it back into a closet.
Closet closet = getDefaultObjectMapper().readValue(Paths.get("./data/Closet.json")
.toFile(), Closet.class);
I am a beginner java programmer and am unsure of how I can approach this issue. I have done some searching with creating custom deserializers, however, I'm unsure of how to implement the deserializer with my nested objects (Clothing nested in Closet) and the fact that there could be an arbitrary number of Clothing in the Closet object.
I have the same problem with another class called StyleBoard which is a list of Outfit (another class), and an Outfit, is a list of Clothing. An example of the jsonfile for StyleBoard written to .json file is
> 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
}
This is the Error Message I am getting:
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)
A problem that I noticed that might be causing this, is that when I write my Closet Object to a .json File, it adds an extra field "numberOfClothing" as you can see above. This is not part of my Closet constructor, however I do have a getter called getNumberOfClothing. When I change the name of this getter to getCollectionSize, this extra field added to my json becomes "collectionSize".
Why is this extra field being created? How can I prevent it? I think this extra field is causing the problem with reconstructing the object from json.
答案1
得分: 0
首先,您需要根据您的JSON创建类。您需要创建一个名为Closet的类。
请注意,我使用Lombok来提高代码的简洁性,即消除了需要创建getter、setter和toString方法的需要。如果您不使用Lombok,必须创建这些getter和setter,因为Jackson库将使用这些getter和setter。
@Getter
@Setter
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Closet {
// 您需要创建getter和setter方法。
// 我使用Lombok,所以不需要手动创建
List<Cloth> clothes = new ArrayList<>();
Integer numberOfClothing;
}
接下来是一个Cloth类:
@Getter
@Setter
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Cloth {
String name;
String type;
String color;
Float size;
Boolean needsWashing;
}
如果您需要为类成员命名不同于JSON字段,您可以使用@JsonProperty("json field name")
进行注释,例如:
@JsonProperty("name")
String clothName;
然后是实际的转换代码:
Closet closet = JsonUtil.fromJson(jsonStr, Closet.class);
其中JsonUtil类包含以下静态方法(使用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
至于Styleboard,由于步骤与Closet类完全相同,我将留给您自行处理。
英文:
First, you need to create the classes based on your json.
You need to create a Closet class.
Fyi, I use Lombok for conciseness i.e. remove the need of getters, setters, and toString methods. If you don't use Lombok, you must create the getters and setters as the Jackson library will make use of this getters and setters.
@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;
}
Then a Cloth class:
@Getter
@Setter
@ToString
@FieldDefaults(level= AccessLevel.PRIVATE)
public class Cloth {
String name;
String type;
String color;
Float size;
Boolean needsWashing;
}
if you need to name your class member different from the json field, you can annotate with @JsonProperty("json field name") e.g.
@JsonProperty("name")
String clothName;
Then the actual conversion code:
Closet closet = JsonUtil.fromJson(jsonStr, Closet.class);
where JsonUtil class contains this static method (using 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());
}
}
You can get jackson library via maven dependency below:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
For jackson tutorial, you can google it. Below is just one of the example:
https://www.baeldung.com/jackson-object-mapper-tutorial
And I leave the Styleboard for you as the steps are exactly the same as the Closet class.
答案2
得分: 0
问题出在我的Clothing类没有带有空参数的构造函数。我只是添加了另一个构造函数。
对于额外的未识别字段"numberOfclothing"的问题是因为我的Closet和StyleBoard类都有用于获取值的"getters",但这些值并不是字段的一部分,而是使用List类型的字段的大小。我只需更改这些方法的名称,不包括"get"作为其名称的一部分。
英文:
The issue is with my Clothing class not having a constructor with empty parameters. I simply added another Constructor.
The issue with the additional unrecognized field "numberOfclothing" is due to my Closet and StyleBoard Classes both having "getters" for values that weren't part of a field but for the size of fields using the List type. I simply had to change the name of these methods to not include "get" as part of their name.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论