【问题标题】:Jackson deserialization issues with arrays and objects数组和对象的杰克逊反序列化问题
【发布时间】:2013-04-09 19:23:52
【问题描述】:

我遇到了一个问题,我的 JSON 响应可以是对象或对象数组

具有单个值的 Foobar 示例:

{
    "foo": {"msg": "Hello World" }
}

带有数组的Foobar示例:

{
    "foo": [
           { "msg": "Hello World" },
           { "msg": "Goodbye World" }
         ]
}

我希望将单个值强制转换为任何数组,但到目前为止,我发现将所有单个值转换为数组的唯一方法。


ACCEPT_SINGLE_VALUE_AS_ARRAY

http://wiki.fasterxml.com/JacksonFeaturesDeserialization


我一直在寻找对单个属性执行相同操作的注释,但到目前为止 google 还没有找到任何示例。

以前有没有人遇到过这个问题,我真的不想将所有内容都重写为数组以使 RestTemplate 与有问题的服务一起工作。

【问题讨论】:

    标签: java spring jackson


    【解决方案1】:

    我希望将单个值强制放入任何数组,但到目前为止,唯一的 我发现将所有单个值转换为数组的方式

    这根本不应该是这样。 ACCEPT_SINGLE_VALUE_AS_ARRAY 属性 为给定的 ObjectMapper 开启/关闭,但其行为完全由 JSON 值映射到的目标属性控制。

    • 启用 ACCEPT_SINGLE_VALUE_AS_ARRAY 时,将 JSON 值映射到 Java 集合属性不会导致错误。
    • 启用 ACCEPT_SINGLE_VALUE_AS_ARRAY 时,将 JSON 值映射到 Java 基本属性(也)不会导致错误。

    如下代码所示:

    class Foo {
        private String msg;
    
        // Constructor, setters, getters
    }
    
    class Holder {
        private List<Foo> foo;
        private Foo other;
    
        // Constructors, setters, getters
    }
    
    public class FooTest {
    
        @org.junit.Test
        public void testCollectionFromJSONValue() throws Exception {
            final InputStream stream = Thread.currentThread()
                    .getContextClassLoader().getResourceAsStream("foo.json");
    
            final String json = IOUtils.toString(stream);
    
            final ObjectMapper mapper = new ObjectMapper();
            mapper.configure(
                    DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY,
                    true);
            final Holder holder = mapper.readValue(json, Holder.class);
            System.out.println(holder);
        }
    }
    

    这依赖于以下 JSON:

    {
        "foo": {
            "msg": "Hello World"
        },
        "other": {
            "msg": "Goodbye"
        }
    }
    

    运行代码将显示“foo”属性成功反序列化为列表,而“other”属性被反序列化为(基本)Foo 类型。

    【讨论】:

    • 谢谢,这已经是漫长的一天,我可能应该想通了>。
    • 有没有办法在全局范围内执行此操作(即针对整个应用程序),即在代码本身之外进行配置?
    【解决方案2】:

    我遇到了同样的问题,并且很难找到一种解决方案来以这种方式配置我的 RestTemplate。因为您并不总是想实例化和更改 objectMapper...所以这是我的解决方案:

    <bean id="myRestTemplate" class="org.springframework.web.client.RestTemplate">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper" ref="jacksonObjectMapper" />
                </bean>
            </list>
        </property>
    </bean>
    <bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="jacksonObjectMapper" />
        <property name="targetMethod" value="configure" />
        <property name="arguments">
            <list>
                <value type="com.fasterxml.jackson.databind.DeserializationFeature">ACCEPT_SINGLE_VALUE_AS_ARRAY</value>
                <value>true</value>
            </list>
        </property>
    </bean>
    

    然后,您可以通过将这个预配置的 RestTemplate 注入到您的代码中来使用它:

    @Autowired
    private RestTemplate myRestTemplate;
    

    【讨论】:

      【解决方案3】:

      使用 RestTemplate 时解决此问题的最佳方法。

      import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
      ObjectMapper objectMapper = new ObjectMapper()
                  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                  .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, false);
      MappingJackson2HttpMessageConverter jacksonMappingConverter 
                    = new MappingJackson2HttpMessageConverter(objectMapper);
      restTemplate.getMessageConverters().add(0, jacksonMappingConverter);
      

      对于要解析的元素,使用注释,可以是对象或数组,定义如下

      import com.fasterxml.jackson.annotation.JsonFormat;
      import com.fasterxml.jackson.annotation.JsonProperty;
      public class ParentObject{
        @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
        @JsonProperty("InnerObject")
       private List<InnerObject> innerObject;
      

      }

      如果您不想向 restTemplate 添加新的映射器,请更改现有的映射器以支持用例

      List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
              for (HttpMessageConverter<?> httpMessageConverter : messageConverters) {
                  if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {
                      MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) httpMessageConverter;
                      mappingJackson2HttpMessageConverter.getObjectMapper()
                              .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
                  }
              } 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-11
        • 2018-03-16
        • 2018-12-18
        • 2014-12-18
        • 2016-04-18
        • 1970-01-01
        • 2019-11-15
        相关资源
        最近更新 更多