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

Map with object and list of objects to Json

我有两个主要模型类:CustomerProduct

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"}
        ]
    }
]

提示:分离域对象(CustomerProduct)和用于数据传输的表示对象(CustomerDtoProductDto)通常也是一个好主意,因为它允许为任何具体表示实现创建表示(一个用于各种 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"}
        ]
    ]
]