【问题标题】:How to create generic method for ObjectNode put如何为 ObjectNode put 创建通用方法
【发布时间】:2022-01-19 23:50:31
【问题描述】:

我有一个用例,我需要支持将多种类型的值插入到 ObjectNode(Jackson ObjectMapper 的)中,但是当我尝试使用泛型时,它会出现错误 Cannot resolve method 'put(java.lang.String, T)'

    public static <T> JsonNode of(String key, T value) {
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode root = objectMapper.createObjectNode();
        root.put(key, value); // <== error shows up on this line


        return root;
}

底层的 ObjectNode 确实支持许多类型的值,例如&lt;String, Boolean&gt;&lt;String, String&gt;&lt;String, Integer&gt;

完整文档Jackson

这样做的正确方法是什么?谢谢。

【问题讨论】:

    标签: java generics jackson


    【解决方案1】:

    问题是通过类型擦除,编译器会为你的泛型方法生成下面的方法。

        public static JsonNode of(String key, Object value) {
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode root = objectMapper.createObjectNode();
            root.put(key, value); // <== error shows up on this line
            return root;
    }
    

    根据 Jackson 文档,没有这样的方法 put(String key, Object value)

    我不会调用方法of(String key, T value),而是执行以下操作:

      ObjectNode root = new ObjectMapper().createObjectNode();
      root.put(key, value);
    

    或者你可以写几个重载方法of(...)

    【讨论】:

    • 是的,of(...) 的多种方法显然有效。我只是想看看是否有更简单的方法可以使用 Java 的泛型支持来创建这些 JsonNode :)
    【解决方案2】:

    还有另一种适用于任何 json 对象的方法:

    Map<String, Object> map = new ObjectMapper().readValue(json, new TypeReference<HashMap<String,Object>>() {});
    

    值中的对象可以是任何值对象(StringInteger 等)、另一个作为嵌套对象的Map&lt;String, Object&gt;List&lt;Object&gt;/List&lt;Map&lt;String, Object&gt;&gt; 等任何深度。

    【讨论】:

      【解决方案3】:

      如果您查看 ObjectNode#put 方法的签名,您会看到

      public JsonNode put(String fieldName, JsonNode value)
      

      第二个参数实际上绑定到特定类型,它是JsonNode ,而不仅仅是任何T(很像您的方法允许的)。

      因此,由于类型擦除 - 编译器必须确保您在编译时提供兼容的类型,我建议您对方法进行适当的限制,例如:

      public static <T extends JsonNode> JsonNode of(String key, T value) {
          ObjectMapper objectMapper = new ObjectMapper();
          ObjectNode root = objectMapper.createObjectNode();
          root.set(key, value);
      
          return root;
      }
      

      确保您的方法仅接受 JsonNode 或其后代。


      小注1:每次调用方法时都实例化ObjectMapper并不是一件好事,因为创建它的对象有点昂贵,最好将其设为字段。

      小注2:ObjectNode#put方法从2.4版本开始被弃用,建议改用ObjectNode#set

      【讨论】:

      • 使用 后,当我以 JsonNode.of("key", "value") 的形式调用调用时,它会报错 no instance(s) of type variable(s) exist so that String conforms to JsonNode
      • 当然它会抱怨——现在你需要你的论点是JsonNode的某个实例。 ObjectNode 有 16+ 个版本的 put 方法,不幸的是它们不能被推广。简而言之,如果允许这种通用方法 of(String key, T value) 并以某种方式解析参数的运行时类型以调用适当的 ObjectNode#put 版本 - 如果你用 null 参数调用你 of 会做什么?您想要的是动态方法解析,而泛型在这里无济于事,您要么需要使用大量重载方法,要么使用反射/方法句柄
      • 在调用&lt;T extends JsonNode&gt; JsonNode of(String key, T value)之前,可以在运行时使用JsonNodeFactory.instance.XXXXX方法构造一个合适的JsonNode,这样JsonNode.of("key", "value")就会变成JsonNode.of("key", JsonNodeFactory.instance.textNode("value")),例如
      猜你喜欢
      • 1970-01-01
      • 2017-10-20
      • 1970-01-01
      • 2016-03-14
      • 2020-06-30
      • 1970-01-01
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      相关资源
      最近更新 更多