【问题标题】:Map with object and list of objects to Json将对象和对象列表映射到 Json
【发布时间】:2021-03-01 23:04:34
【问题描述】:

我有两个主要的模型类: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());
        }
    }

【问题讨论】:

  • 第一个 JSON 写的看起来坏了,而第二个“JSON”不是一个,这是无效的语法
  • @ETO 首先我尝试编写自己的语法,但它没有用,所以我使用我的toJson 方法自动编写此文件并查看它的语法,但如您所见,坏了,帖子里有我的toJson方法
  • @Tom 你能告诉我正确的语法吗?
  • 您需要将您的产品列表存储在 Customer 中。

标签: java json gson


【解决方案1】:

@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>> 转换为 List<CustomerDto> 并返回(可能通过使用 mapper- MapStruct 等生成器)。

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

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

【讨论】:

    猜你喜欢
    • 2019-05-27
    • 2014-11-07
    • 2012-04-23
    • 2013-03-11
    • 1970-01-01
    • 2021-05-18
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多