将对象和对象列表映射到 Json
Map with object and list of objects to Json
我有两个主要模型类:Customer
和Product
public class Customer {
String name;
String surname;
int age;
BigDecimal cash;
}
public class Product {
String name;
Category category;
BigDecimal price;
}
我想用 Map<Customer, List<Product>>
构建 json
文件
当我使用正确工作的方法写入 json
文件数据时 - 我确信这一点 - json
文件显示此语法
{
"Customer{name\u003d\u0027Custo1\u0027, surname\u003d\u0027Surname\u0027, age\u003d18, cash\u003d1200}": [
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
}
然后当我想读取这个文件时,出现错误Exception in thread "main" java.util.NoSuchElementException: No value present
所以我认为json文件中的Customer
语法无法识别。
所以我尝试用下面的语法自己将数据写入 json
文件,但它不起作用
[
{
"name": "Abc",
"surname": "Def",
"age": 14,
"cash": "2000"
}
:
[
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
]
json转换器方法:
public void toJson(final T item) {
try (FileWriter fileWriter = new FileWriter(jsonFilename)) {
fileWriter.write(gson.toJson(item));
} catch (Exception e) {
throw new ValidatorException(e.getMessage());
}
}
@Tom 在您遇到的问题上是正确的。我将解释原因并提出另一种解决方案。
你的第一个 JSON 在技术上是一个有效的 JSON 但它不能被反序列化,因为映射键是 Gson 默认使用的 Customer.toString()
方法的结果。这就是为什么它看起来很奇怪,就像一个调试字符串,并且不能被反序列化:几乎总是没有办法从 toString()
结果中恢复对象(toString
主要是为debugging/logging 目的是提供有关特定对象状态的基本 信息,根本不需要公开其所有内部结构)。
你的第二个 JSON 是 invalid JSON。期间.
Tom 关于将产品列表作为客户的一部分的建议 class 完全没问题。像这样实现它可以让您将所有内容序列化为这样的列表:
[
{
"name": "john",
"products": [
{"name": "prod1"},
{"name": "prod2"}
]
}
]
提示:分离域对象(Customer
和 Product
)和用于数据传输的表示对象(CustomerDto
和 ProductDto
)通常也是一个好主意,因为它允许为任何具体表示实现创建表示(一个用于各种 JSON 实现库,两个用于面向其他格式的工具,第三个用于持久性,四个用于 UI 视图等),所以它可能是实现就像将 Map<Customer, List<Product>>
转换为 List<CustomerDto>
并返回(可能通过使用像 MapStruct 这样的映射器生成器)。
如果由于某种原因无法重组您的域 classes 或创建 Gson 友好的 DTO 映射,或者您可以保持它尽可能简单并且您可以没有那么琐碎的 JSON 结构(只要您了解此解决方案中格式的含义:进化、分布等),那么您可以启用特殊的 Gson 模式来支持这种映射。它生成可以序列化和反序列化的有效 JSONs,但它的实现方式对我来说看起来有点反模式,因为由于使用数组作为数据容器而丢失了语义。
@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Customer {
final String name;
}
@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Product {
final String name;
}
public final class MapTest {
private static final Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
private static final TypeToken<Map<Customer, List<Product>>> customerToProducts = new TypeToken<Map<Customer, List<Product>>>() {};
@Test
public void test() {
final Map<Customer, List<Product>> ordersBefore = ImmutableMap.of(
new Customer("john"), ImmutableList.of(new Product("prod1"), new Product("prod2"))
);
final String json = gson.toJson(ordersBefore, customerToProducts.getType());
Assertions.assertEquals("[[{\"name\":\"john\"},[{\"name\":\"prod1\"},{\"name\":\"prod2\"}]]]", json);
final Map<Customer, List<Product>> ordersAfter = gson.fromJson(json, customerToProducts.getType());
Assertions.assertEquals(ordersBefore, ordersAfter);
}
}
注意它生成的JSON是这样的(索引0
表示键,索引1
表示值):
[
[
{"name": "john"},
[
{"name": "prod1"},
{"name": "prod2"}
]
]
]
我有两个主要模型类:Customer
和Product
public class Customer {
String name;
String surname;
int age;
BigDecimal cash;
}
public class Product {
String name;
Category category;
BigDecimal price;
}
我想用 Map<Customer, List<Product>>
构建 json
文件
当我使用正确工作的方法写入 json
文件数据时 - 我确信这一点 - json
文件显示此语法
{
"Customer{name\u003d\u0027Custo1\u0027, surname\u003d\u0027Surname\u0027, age\u003d18, cash\u003d1200}": [
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
}
然后当我想读取这个文件时,出现错误Exception in thread "main" java.util.NoSuchElementException: No value present
所以我认为json文件中的Customer
语法无法识别。
所以我尝试用下面的语法自己将数据写入 json
文件,但它不起作用
[
{
"name": "Abc",
"surname": "Def",
"age": 14,
"cash": "2000"
}
:
[
{
"name": "prod1",
"category": "CLOTHES",
"price": 12000
},
{
"name": "prod2",
"category": "ELECTRONIC",
"price": 15000
}
]
]
json转换器方法:
public void toJson(final T item) {
try (FileWriter fileWriter = new FileWriter(jsonFilename)) {
fileWriter.write(gson.toJson(item));
} catch (Exception e) {
throw new ValidatorException(e.getMessage());
}
}
@Tom 在您遇到的问题上是正确的。我将解释原因并提出另一种解决方案。
你的第一个 JSON 在技术上是一个有效的 JSON 但它不能被反序列化,因为映射键是 Gson 默认使用的 Customer.toString()
方法的结果。这就是为什么它看起来很奇怪,就像一个调试字符串,并且不能被反序列化:几乎总是没有办法从 toString()
结果中恢复对象(toString
主要是为debugging/logging 目的是提供有关特定对象状态的基本 信息,根本不需要公开其所有内部结构)。
你的第二个 JSON 是 invalid JSON。期间.
Tom 关于将产品列表作为客户的一部分的建议 class 完全没问题。像这样实现它可以让您将所有内容序列化为这样的列表:
[
{
"name": "john",
"products": [
{"name": "prod1"},
{"name": "prod2"}
]
}
]
提示:分离域对象(Customer
和 Product
)和用于数据传输的表示对象(CustomerDto
和 ProductDto
)通常也是一个好主意,因为它允许为任何具体表示实现创建表示(一个用于各种 JSON 实现库,两个用于面向其他格式的工具,第三个用于持久性,四个用于 UI 视图等),所以它可能是实现就像将 Map<Customer, List<Product>>
转换为 List<CustomerDto>
并返回(可能通过使用像 MapStruct 这样的映射器生成器)。
如果由于某种原因无法重组您的域 classes 或创建 Gson 友好的 DTO 映射,或者您可以保持它尽可能简单并且您可以没有那么琐碎的 JSON 结构(只要您了解此解决方案中格式的含义:进化、分布等),那么您可以启用特殊的 Gson 模式来支持这种映射。它生成可以序列化和反序列化的有效 JSONs,但它的实现方式对我来说看起来有点反模式,因为由于使用数组作为数据容器而丢失了语义。
@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Customer {
final String name;
}
@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Product {
final String name;
}
public final class MapTest {
private static final Gson gson = new GsonBuilder()
.enableComplexMapKeySerialization()
.create();
private static final TypeToken<Map<Customer, List<Product>>> customerToProducts = new TypeToken<Map<Customer, List<Product>>>() {};
@Test
public void test() {
final Map<Customer, List<Product>> ordersBefore = ImmutableMap.of(
new Customer("john"), ImmutableList.of(new Product("prod1"), new Product("prod2"))
);
final String json = gson.toJson(ordersBefore, customerToProducts.getType());
Assertions.assertEquals("[[{\"name\":\"john\"},[{\"name\":\"prod1\"},{\"name\":\"prod2\"}]]]", json);
final Map<Customer, List<Product>> ordersAfter = gson.fromJson(json, customerToProducts.getType());
Assertions.assertEquals(ordersBefore, ordersAfter);
}
}
注意它生成的JSON是这样的(索引0
表示键,索引1
表示值):
[
[
{"name": "john"},
[
{"name": "prod1"},
{"name": "prod2"}
]
]
]