将对象和对象列表映射到 Json

时间:2021-03-01 23:04:34

标签: java json gson

我有两个主要的模型类:CustomerProduct

public class Customer {
    String name;
    String surname;
    int age;
    BigDecimal cash;
}

public class Product {
    String name;
    Category category;
    BigDecimal price;
}

我想用 json 构建 Map<Customer, List<Product>> 文件 当我使用我的方法正确写入 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());
        }
    }

1 个答案:

答案 0 :(得分:2)

@Tom 对您所面临的问题是正确的。我会解释原因并建议另一种解决方案。

您的第一个 JSON 在技术上是有效的 JSON,但无法反序列化,因为映射键是 Gson 默认使用的 Customer.toString() 方法的结果。这就是为什么它看起来很奇怪,就像一个调试字符串,并且不能反序列化:几乎总是无法从 toString() 结果中恢复对象(toString 主要用于调试/记录目的提供基本有关特定对象状态的信息,根本不需要公开其所有内部结构)。

您的第二个 JSON 是 invalid JSON。期间。

Tom 建议将产品列表作为客户类别的一部分是完全正确的。像这样实现它可以让您将所有内容序列化为这样的列表:

[
    {
        "name": "john",
        "products": [
            {"name": "prod1"},
            {"name": "prod2"}
        ]
    }
]

提示:分离域对象(CustomerProduct)和数据传输的表示对象(CustomerDtoProductDto)通常也是一个好主意,因为它允许为任何具体的表示实现创建表示(一个用于各种 JSON 实现库,两个用于面向其他格式的工具,第三个用于持久性,四个用于 UI 视图等),因此它的实现方式可能类似于将 Map<Customer, List<Product>> 转换为 { {1}} 并返回(可能使用 MapStruct 等映射器生成器)。

如果由于某种原因无法重新组织您的域类或创建 Gson 友好的 DTO 映射,或者您可以保持它尽可能简单并且您没有那种微不足道的 JSON 结构(只要您了解此解决方案中格式的含义:进化、分布等),那么您就可以启用特殊的 Gson 模式来支持这种地图。它生成可以序列化和反序列化的有效 JSON,但它的实现方式在我看来有点反模式,因为使用数组作为数据容器会丢失语义。

List<CustomerDto>
@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Customer {

    final String name;

}
@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Product {

    final String name;

}

注意它生成这样的 JSON(索引 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); } } 表示键,索引 0 表示值):

1