概述

在这个教程中,我们将讲解如何使用Jackson JSON视图来序列化/反序列化对象,自定义视图,并最终集成到Spring框架。

2. 使用JSON视图进行序列化

首先,我们来看一个简单的例子:使用@JsonView注解序列化对象

这是我们的视图:

public class Views {
    public static class Public {
    }
}

以及“User”实体:

public class User {
    public int id;

    @JsonView(Views.Public.class)
    public String name;
}

现在,让我们使用我们的视图序列化一个“User”实例:

@Test
public void whenUseJsonViewToSerialize_thenCorrect() 
  throws JsonProcessingException {
 
    User user = new User(1, "John");

    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);

    String result = mapper
      .writerWithView(Views.Public.class)
      .writeValueAsString(user);

    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("1")));
}

请注意,由于我们正在使用特定的视图进行序列化,所以只看到正确字段被序列化。

还要注意,默认情况下,所有未明确标记为视图一部分的属性都会被序列化。我们通过便捷的DEFAULT_VIEW_INCLUSION特性禁用了这种行为。

3. 使用多个JSON视图

接下来,我们看看如何使用多个JSON视图,每个视图包含不同的字段,如下例所示:

这里有两个视图,Internal继承自Public,内部视图扩展公共视图:

public class Views {
    public static class Public {
    }

    public static class Internal extends Public {
    }
}

而我们的“Item”实体中,“Public”视图只包括idname字段:

public class Item {
 
    @JsonView(Views.Public.class)
    public int id;

    @JsonView(Views.Public.class)
    public String itemName;

    @JsonView(Views.Internal.class)
    public String ownerName;
}

如果我们使用Public视图进行序列化,只会将idname序列化为JSON:

@Test
public void whenUsePublicView_thenOnlyPublicSerialized() 
  throws JsonProcessingException {
 
    Item item = new Item(2, "book", "John");

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper
      .writerWithView(Views.Public.class)
      .writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("2"));

    assertThat(result, not(containsString("John")));
}

但如果使用Internal视图进行序列化,所有字段都将作为JSON输出的一部分:

@Test
public void whenUseInternalView_thenAllSerialized() 
  throws JsonProcessingException {
 
    Item item = new Item(2, "book", "John");

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper
      .writerWithView(Views.Internal.class)
      .writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("2"));

    assertThat(result, containsString("John"));
}

4. 使用JSON视图进行反序列化

现在,我们来看看如何使用JSON视图反序列化对象——特别是User实例:

@Test
public void whenUseJsonViewToDeserialize_thenCorrect() 
  throws IOException {
    String json = "{"id":1,"name":"John"}";

    ObjectMapper mapper = new ObjectMapper();
    User user = mapper
      .readerWithView(Views.Public.class)
      .forType(User.class)
      .readValue(json);

    assertEquals(1, user.getId());
    assertEquals("John", user.getName());
}

请注意,我们使用readerWithView() API创建了一个使用给定视图的ObjectReader

5. 自定义JSON视图

接下来,我们将了解如何定制JSON视图。在下一个示例中,我们想让User的“name”在序列化结果中大写。

我们将使用BeanPropertyWriterBeanSerializerModifier来定制我们的JSON视图。首先,这是将Username转换为大写的BeanPropertyWriter,名为UpperCasingWriter

public class UpperCasingWriter extends BeanPropertyWriter {
    BeanPropertyWriter _writer;

    public UpperCasingWriter(BeanPropertyWriter w) {
        super(w);
        _writer = w;
    }

    @Override
    public void serializeAsField(Object bean, JsonGenerator gen, 
      SerializerProvider prov) throws Exception {
        String value = ((User) bean).name;
        value = (value == null) ? "" : value.toUpperCase();
        gen.writeStringField("name", value);
    }
}

然后,这是设置User名称BeanPropertyWriter为自定义UpperCasingWriterBeanSerializerModifier

public class MyBeanSerializerModifier extends BeanSerializerModifier{

    @Override
    public List<BeanPropertyWriter> changeProperties(
      SerializationConfig config, BeanDescription beanDesc, 
      List<BeanPropertyWriter> beanProperties) {
        for (int i = 0; i < beanProperties.size(); i++) {
            BeanPropertyWriter writer = beanProperties.get(i);
            if (writer.getName() == "name") {
                beanProperties.set(i, new UpperCasingWriter(writer));
            }
        }
        return beanProperties;
    }
}

现在,让我们使用修改后的序列化器序列化一个User实例:

@Test
public void whenUseCustomJsonViewToSerialize_thenCorrect() 
  throws JsonProcessingException {
    User user = new User(1, "John");
    SerializerFactory serializerFactory = BeanSerializerFactory.instance
      .withSerializerModifier(new MyBeanSerializerModifier());

    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializerFactory(serializerFactory);

    String result = mapper
      .writerWithView(Views.Public.class)
      .writeValueAsString(user);

    assertThat(result, containsString("JOHN"));
    assertThat(result, containsString("1"));
}

6. 在Spring中使用JSON视图

最后,让我们快速了解一下如何在Spring框架中使用JSON视图。我们可以利用@JsonView注解在API级别定制我们的JSON响应。

在以下示例中,我们使用了Public视图响应:

@JsonView(Views.Public.class)
@RequestMapping("/items/{id}")
public Item getItemPublic(@PathVariable int id) {
    return ItemManager.getById(id);
}

响应是:

{"id":2,"itemName":"book"}

当我们使用以下方式使用Internal视图:

@JsonView(Views.Internal.class)
@RequestMapping("/items/internal/{id}")
public Item getItemInternal(@PathVariable int id) {
    return ItemManager.getById(id);
}

响应如下:

{"id":2,"itemName":"book","ownerName":"John"}

如果您想深入了解如何在Spring 4.1中使用视图,可以查看Spring 4.1中的Jackson改进

7. 总结

在这篇简短的教程中,我们了解了Jackson JSON视图和@JsonView注解的基本用法。我们展示了如何使用JSON视图精细控制序列化/反序列化过程,无论是使用单个视图还是多个视图。本教程的完整代码可以在GitHub上找到


« 上一篇: Baeldung周报49
» 下一篇: Baeldung每周评论50