如何在 Jackson 中将原始 json 字符串注入到字段中?

Posted

技术标签:

【中文标题】如何在 Jackson 中将原始 json 字符串注入到字段中?【英文标题】:How to express in Jackson injection of original json string into a field? 【发布时间】:2022-01-02 20:40:27 【问题描述】:

有一个JSON字符串""" "a": 1, "b": "hello" """

我想创建一个模型,包含“a”、“b”和“originalJson”。

class MyModel

    public int a;
    public String b;
    public String originalJson;
 

void test1()
    var payload = """ "a": 1, "b": "hello" """;

    // how to apply Jackson here?
    MyModel model = magicParse(payload, MyModel.class);

    assertEquals(1, model.a);
    assertEquals("hello", model.b);
    assertEquals(payload, model.originalJson);

如果我们将其扩展到更广泛的应用程序

void test2()
    var payload = """ [
          "a": 1, "b": "hello",
          "a": 2, "b": "bye"
    ]
    """;

    // how to apply Jackson here?
    MyModel[] models = magicParse(payload, MyModel[].class);

    var firstModel = model[0]
    assertEquals(1, firstModel.a);
    assertEquals("hello", firstModel.b);
    assertEquals(""""a": 1, "b": "hello"""", firstModel.originalJson);


    var secondModel = model[2]
    assertEquals(2, secondModel.a);
    assertEquals("bye", secondModel.b);
    assertEquals(""""a": 2, "b": "bye"""", secondModel.originalJson);

在 Jackson 中是否有一种自然的方法(注释、配置等)?

【问题讨论】:

我会说你唯一的选择是使用自定义反序列化器。 【参考方案1】:

你可以使用ObjectMapper.readTree():

private static final ObjectMapper MAPPER = new ObjectMapper();

public MyModel magicParse(String payload) 
        throws JsonMappingException, JsonProcessingException 
    JsonNode root = MAPPER.readTree(payload);
    int a = root.get("a").asInt();
    String b = root.get("b").asText();
    return new MyModel(a, b, payload);

您还需要 MyModel 的构造函数(或根据您的情况设置设置器):

public class MyModel 

    public int a;
    public String b;
    public String originalJson;

    MyModel(int a, String b, String originalJson) 
        this.a = a;
        this.b = b;
        this.originalJson = originalJson;
    


【讨论】:

【参考方案2】:

使用自定义反序列化器尝试了几种解决方案,但看起来它们需要大量代码来模糊主要意图。我看到的基本问题是,在反序列化器中,我们可以访问JsonParser,它是有状态的并维护“一次性”算法,因此无法在不干扰正常解析过程的情况下从中获取 JSON 子树/子字符串。

另一种方法更具可读性,尽管我们对有效负载进行两次反序列化可能会对性能产生影响。 想法是获取JsonNode并使用它反序列化模型并进行进一步的转换:在基本反序列化之后和返回结果之前传递根节点。

// utility for mapping
public static <T, R> R readValue(
    String json,
    Class<T> clazz,
    BiFunction<T, JsonNode, R> transformValue
)
    JsonNode node = getObjectMapper().readValue(json, JsonNode.class);
    T value = mapper.treeToValue(node, clazz);
    return transformValue.apply(value, node);



// concrete usage
MyModel getModel(String payload)
    return readValue(
      payload, 
      MyModel.class, 
      (m, node) -> 
          m.payload = node.toString(); 
          return m;
    );

【讨论】:

以上是关于如何在 Jackson 中将原始 json 字符串注入到字段中?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jackson 在对象中包含原始 JSON?

如何使用 Jackson 将原始 JSON 反序列化为 Java 对象

如何在 Jackson 中将对象序列化为 ObjectNode 的值?

如何在python中将正则表达式序列化为json?

Spring Boot (Jackson):如何避免类名被序列化为 JSON

如何在 Python 中将 JSON 字符串转换为字典?