【问题标题】:Jackson json deserialization, ignore root element from jsonJackson json反序列化,忽略json中的根元素
【发布时间】:2012-02-08 20:33:20
【问题描述】:

如何忽略json中的父标签??

这是我的json

String str = "{\"parent\": {\"a\":{\"id\": 10, \"name\":\"Foo\"}}}";

这是要从 json 映射的类。

public class RootWrapper {
  private List<Foo> foos;

  public List<Foo> getFoos() {
    return foos;
  }

  @JsonProperty("a")
  public void setFoos(List<Foo> foos) {
    this.foos = foos;
  }
 }

这是测试 公共类 JacksonTest {

@Test
public void wrapRootValue() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
    mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    String str = "{\"parent\": {\"a\":{\"id\": 10, \"name\":\"Foo\"}}}";

    RootWrapper root = mapper.readValue(str, RootWrapper.class);

    Assert.assertNotNull(root);
}

我得到错误::

 org.codehaus.jackson.map.JsonMappingException: Root name 'parent' does not match expected ('RootWrapper') for type [simple type, class MavenProjectGroup.mavenProjectArtifact.RootWrapper]

我找到了Jackson注解给出的解决方案::

  (a) Annotate you class as below

  @JsonRootName(value = "parent")
  public class RootWrapper {

  (b) It will only work if and only if ObjectMapper is asked to wrap.
    ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);

任务完成!!

Jackson 反序列化方式的另一个问题:(

如果“DeserializationConfig.Feature.UNWRAP_ROOT_VALUE 已配置”,它会解开所有 json,尽管我的类没有使用 @JsonRootName(value = "rootTagInJson") 进行注释,但并不奇怪。

只有当类被 @JsonRootName 注释时,我才想解开根标签,否则不要解开。

下面是展开根标签的用例。

  ###########################################################
     Unwrap only if the class is annotated with @JsonRootName.
  ############################################################

我对 Jackson 源代码的 ObjectMapper 做了一个小改动,并创建了一个新版本的 jar。 1.将此方法放在ObjectMapper中

// Ash:: Wrap json if the class being deserialized, are annotated
// with @JsonRootName else do not wrap.
private boolean hasJsonRootName(JavaType valueType) {
    if (valueType.getRawClass() == null)
        return false;

    Annotation rootAnnotation =  valueType.getRawClass().getAnnotation(JsonRootName.class);
    return rootAnnotation != null;
}


    2. Edit ObjectMapper method :: 
    Replace 
       cfg.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE)
    with
       hasJsonRootName(valueType)

    3. Build your jar file and use it.

【问题讨论】:

  • 这不是一个真正的问题。我们鼓励您提出自己的问题,但建议您以问答对的形式进行。
  • 除了自动包装/解包之外,我发现简单的单属性包装器类可以创造奇迹;或绑定到Map&lt;String,WrappedType&gt;,然后获取唯一条目的值。
  • 我喜欢这个答案!!我只有一个问题?如果我想这样做怎么办: TypeReference ref = new TypeReference>() {} );然后在这里做一些解包。我的问题是 JsonRootName 在这里需要“列表”,但我的对象有一些不同。我不知道如何为 ref 变量添加注释?
  • @StaxMan 的建议实际上非常有用,并且当您可能正在编写 JerseyTest 并意识到 @Ash 的解决方案还不是 apache 源代码的一部分时,这是实现目标的最短路径。我花了几分钟才弄清楚如何在 Java 代码中绑定泛型:myClientResponse.getEntity(new GenericType&lt;Map&lt;String, WrappedType&gt;&gt;(){});

标签: json jackson


【解决方案1】:

取自 https://github.com/FasterXML/jackson-databind 中的 TestRootName.java 的示例可能会提供更好的方法来执行此操作。具体使用 withRootName(""):

private ObjectMapper rootMapper()
{
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
    mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
    return mapper;
}

public void testRootUsingExplicitConfig() throws Exception
{
    ObjectMapper mapper = new ObjectMapper();
    ObjectWriter writer = mapper.writer().withRootName("wrapper");
    String json = writer.writeValueAsString(new Bean());
    assertEquals("{\"wrapper\":{\"a\":3}}", json);

    ObjectReader reader = mapper.reader(Bean.class).withRootName("wrapper");
    Bean bean = reader.readValue(json);
    assertNotNull(bean);

    // also: verify that we can override SerializationFeature as well:
    ObjectMapper wrapping = rootMapper();
    json = wrapping.writer().withRootName("something").writeValueAsString(new Bean());
    assertEquals("{\"something\":{\"a\":3}}", json);
    json = wrapping.writer().withRootName("").writeValueAsString(new Bean());
    assertEquals("{\"a\":3}", json);

    bean = wrapping.reader(Bean.class).withRootName("").readValue(json);
    assertNotNull(bean);
}

【讨论】:

    【解决方案2】:

    我在 Spring 中开发一个安静的应用程序时遇到了类似的问题。我必须支持一个非常异构的 API,其中一些有根元素,另一些没有。我找不到比实时配置此属性更好的解决方案。很遗憾,Jackson 不支持每个类的根元素展开。无论如何,有人可能会觉得这很有帮助。

    @Component
    public class ObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper {
        private void autoconfigureFeatures(JavaType javaType) {
            Annotation rootAnnotation = javaType.getRawClass().getAnnotation(JsonRootName.class);
            this.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, rootAnnotation != null);
        }
    
        @Override
        protected Object _readMapAndClose(JsonParser jsonParser, JavaType javaType) throws IOException, JsonParseException, JsonMappingException {
            autoconfigureFeatures(javaType);
            return super._readMapAndClose(jsonParser, javaType);
        }
    
    }
    

    【讨论】:

    • 我放了代码,我想我需要配置spring才能使用它。你能描述更多如何使用这个类吗?
    【解决方案3】:

    很简单:

    创建对象映射器实例并启用包装和展开根值

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
    

    给你的 DTO 添加@JsonRootName("yourname")注解

    @JsonRootName("root")
    public class YourDto {
        // ...
    }
    

    【讨论】:

      【解决方案4】:

      作为更新 Seagabond 的帖子,如果您希望在写入参数值时具有相同的效果,您可以覆盖额外的写入方法。

      @Component
      public class ObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper {
          private void autoconfigureFeatures(Object value) {
              JavaType javaType = _typeFactory.constructType(value.getClass());
              autoconfigureFeatures(javaType);
          }
          private void autoconfigureFeatures(JavaType javaType) {
              Annotation rootAnnotation = javaType.getRawClass().getAnnotation(JsonRootName.class);
              this.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, rootAnnotation != null);
          }
      
          @Override
          public void writeValue(DataOutput out, Object value) throws IOException {
              autoconfigureFeatures(value);
              super.writeValue(out, value);
          }
      
          @Override
          public void writeValue(Writer w, Object value) throws IOException, JsonGenerationException, JsonMappingException {
              autoconfigureFeatures(value);
              super.writeValue(w, value);
          }
      
          @Override
          public byte[] writeValueAsBytes(Object value) throws JsonProcessingException {
              autoconfigureFeatures(value);
              return super.writeValueAsBytes(value);
          }
      
          @Override
          public String writeValueAsString(Object value) throws JsonProcessingException {
              autoconfigureFeatures(value);
              return super.writeValueAsString(value);
          }
      
          @Override
          protected Object _readMapAndClose(JsonParser jsonParser, JavaType javaType) throws IOException, JsonParseException, JsonMappingException {
              autoconfigureFeatures(javaType);
              return super._readMapAndClose(jsonParser, javaType);
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        我也遇到过这种类型的问题。我在配置类中添加了下面的代码行,我在其中定义了 RestTemplate 及其 MessageConverters 的配置。

        Annotation rootAnnotation = mapper.getTypeFactory().getClass().getAnnotation(JsonRootName.class); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, Objects.nonNull(rootAnnotation));

        基本上这个配置将帮助 Mapper 识别提供的类是否用'@JsonRootName'注释,如果不是,则不需要搜索根。 希望对您和其他人有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-07-27
          • 2013-10-25
          • 2012-02-02
          • 1970-01-01
          • 2020-11-06
          • 2016-03-22
          • 2015-02-28
          • 2022-01-01
          相关资源
          最近更新 更多