本文内容

  • 高级 Jackson Marshalling
    • 只序列化符合自定义标准的字段
    • 把 Enums 序列化成 JSON 对象
    • JsonMappingException(没有找到类的序列化器)
    • Jackson – 自定义序列化器
  • 高级 Jackson Unmarshalling
    • Unmarshall 成 Collection/Array
    • Jackson – 自定义反序列化器
  • 演示
  • 参考资料

本文使用 Jackson 2,包括 jackson-annotations-2.4.0.jar、jackson-core-2.4.1.jar 和 jackson-databind-2.4.1.jar 这三个库。

貌似太理论的东西,看得人也比较少,都喜欢看实际功能的东西,不过啊,只关注功能、理论太弱的人,基本没前途~

下载 Demo

下载 Jackson 2

高级 Jackson Marshalling


介绍高级的序列化配置和优化处理条件、各种数据类型以及自定义 Jackson 异常。

只序列化符合自定义标准的字段

如何使用 Jackson 只序列化一个符合指定的、自定义标准的字段。

例如,我只想序列化一个正整数,否则,就忽略整个整数。

  • 使用 Jackson Filter 控制序列化过程

首先,我们在实体上用 @JsonFilter 注解定义过滤器:

@JsonFilter("myFilter")

public class MyDto {

    private int intValue;

 

    public MyDto() {

        super();

    }

 

    public int getIntValue() {

        return intValue;

    }

 

    public void setIntValue(int intValue) {

        this.intValue = intValue;

    }

}

然后,定义我们自己的 PropertyFilter

PropertyFilter theFilter = new SimpleBeanPropertyFilter() {

   @Override

   public void serializeAsField

    (Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)

     throws Exception {

      if (include(writer)) {

         if (!writer.getName().equals("intValue")) {

            writer.serializeAsField(pojo, jgen, provider);

            return;

         }

         int intValue = ((MyDtoWithFilter) pojo).getIntValue();

         if (intValue >= 0) {

            writer.serializeAsField(pojo, jgen, provider);

         }

      } else if (!jgen.canOmitFields()) { // since 2.3

         writer.serializeAsOmittedField(pojo, jgen, provider);

      }

   }

   @Override

   protected boolean include(BeanPropertyWriter writer) {

      return true;

   }

   @Override

   protected boolean include(PropertyWriter writer) {

      return true;

   }

};

这个 filter 包含一个 intValue 字段是否被序列化的逻辑,这取决于 intValue 的值。

现在,在 ObjectMapper 里调用 filter,序列化这个实体:

FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", theFilter);

MyDto dtoObject = new MyDto();

dtoObject.setIntValue(-1);

 

ObjectMapper mapper = new ObjectMapper();

String dtoAsString = mapper.writer(filters).writeValueAsString(dtoObject);

最后,测试 intValue 字段是否在 JSON 输出中:

assertThat(dtoAsString, not(containsString("intValue")));

filter 功能很强大,当用 Jackson 序列化复杂对象时,可以非常灵活地自定义 JSON。

把 Enums 序列化成 JSON 对象

如何用 Jackson 2 把 Java Enum 序列化。

  • 控制枚举表示

定义下面枚举:

public enum Type {

    TYPE1(1, "Type A"), TYPE2(2, "Type 2");

 

    private Integer id;

    private String name;

 

    private Type(final Integer id, final String name) {

        this.id = id;

        this.name = name;

    }

 

    // standard getters and setters

}

  • 默认的枚举表示

默认情况,Jackson 会把 Java 枚举表示成一个字符串,例如:

new ObjectMapper().writeValueAsString(Type.TYPE1);

结果:

"TYPE1"

当把这个枚举序列化成 JSON 对象时,我们想得到如下所示:

{"name":"Type A","id":1}

  • 枚举作为 Json 对象

用 Jackson 2.1.2 – 下面的配置可以得到上面的表示 – 这是通过在枚举级别上使用 @JsonFormat 注解:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)

public enum Type { ... }

当序列化上面枚举,就会得到正确的结果:

{"name":"Type A","id":1}

  • 枚举和 @JsonValue

另外一种控制方式是使用 @JsonValue 注解:

public enum TypeEnumWithValue {

    TYPE1(1, "Type A"), TYPE2(2, "Type 2");

 

    private Integer id;

    private String name;

 

    private TypeEnumWithValue(final Integer id, final String name) {

        this.id = id;

        this.name = name;

    }

 

    ...

 

    @JsonValue

    public String getName() {

        return name;

    }

}

我们在这里说的是,getName 是此枚举的实际表示;所以:

String enumAsString = mapper.writeValueAsString(TypeEnumWithValue.TYPE1);

assertThat(enumAsString, is("\"Type A\""));

  • 自定义枚举序列化器

在 Jackson 2.1.2 以前,或是对枚举需要更多的自定义 – 我们可以使用一个自定义的 Jackson 序列化器 – 首先,我们需要定义它:

public class TypeSerializer extends JsonSerializer<TypeEnum> {

 

    public void serialize

      (TypeEnumWithCustomSerializer value, JsonGenerator generator, SerializerProvider provider) 

      throws IOException, JsonProcessingException {

        generator.writeStartObject();

        generator.writeFieldName("id");

        generator.writeNumber(value.getId());

        generator.writeFieldName("name");

        generator.writeString(value.getName());

        generator.writeEndObject();

    }

}

我们现在将绑定序列化器,在类上应用它:

@JsonSerialize(using = TypeSerializer.class)

public enum TypeEnum { ... }

JsonMappingException(没有找到类的序列化器)

分析序列化没有 getter 的实体,以及 Jackson JsonMappingException 异常的解决方案。

  • 问题

默认情况,Jackson 2 运行在 public 或具有 public getter 方法的字段上 – 序列化 private 或 private 包内所有字段的一个实体将会失败:

ublic class MyDtoNoAccessors {

    String stringValue;

    int intValue;

    boolean booleanValue;

 

    public MyDtoNoAccessors() {

        super();

    }

 

    // no getters

}

@Test(expected = JsonMappingException.class)

public void givenObjectHasNoAccessors_whenSerializing_thenException() 

  throws JsonParseException, IOException {

    String dtoAsString = new ObjectMapper().writeValueAsString(new MyDtoNoAccessors());

 

    assertThat(dtoAsString, notNullValue());

}

异常信息如下所示:

com.fasterxml.jackson.databind.JsonMappingException: 

No serializer found for class dtos.MyDtoNoAccessors 

and no properties discovered to create BeanSerializer 

(to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )

  • 解决方案

显而易见的解决方案是为字段添加 getter ,当然,如果实体处于我们控制之下时。如果不是这种情况,修改实体的源代码就是不可能的,那么 Jackson 为我们提供了几个可选方案。

  • 1,全局自动检测任何可见的字段

第一个解决方案是,全局配置 ObjectMapper 来检测所有字段,而不管它们是否可见:

objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

这将允许 private 和 private 包的字段被检测到,即便没有 getter,序列化就会正确地执行:

@Test

public void givenObjectHasNoAccessors_whenSerializingWithAllFieldsDetected_thenNoException() 

  throws JsonParseException, IOException {

    ObjectMapper objectMapper = new ObjectMapper();

    objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

    String dtoAsString = objectMapper.writeValueAsString(new MyDtoNoAccessors());

 

    assertThat(dtoAsString, containsString("intValue"));

    assertThat(dtoAsString, containsString("stringValue"));

    assertThat(dtoAsString, containsString("booleanValue"));

}

  • 2,在类的级别上检测所有字段

Jackson 2 的另一个选择是,通过 @JsonAutoDetect 注解,在类的级别上控制字段的可见性,而不是进行全局配置:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)

public class MyDtoNoAccessors { ... }

对这个注解,序列化不能正确执行:

@Test

public void givenObjectHasNoAccessorsButHasVisibleFields_whenSerializing_thenNoException() 

  throws JsonParseException, IOException {

    ObjectMapper objectMapper = new ObjectMapper();

    String dtoAsString = objectMapper.writeValueAsString(new MyDtoNoAccessors());

 

    assertThat(dtoAsString, containsString("intValue"));

    assertThat(dtoAsString, containsString("stringValue"));

    assertThat(dtoAsString, containsString("booleanValue"));

}

Jackson – 自定义序列化器

如何用 Jackson 2 通过自定义序列化器序列化一个 Java 实体。

  • 标准的序列化

定义两个简单实体,如何用 Jackson 序列化它们,不使用任何的自定义逻辑:

public class User {

    public int id;

    public String name;

}

public class Item {

    public int id;

    public String itemName;

    public User owner;

}

现在,序列化 Item 实体(里边还包含 User 实体):

Item myItem = new Item(1, "theItem", new User(2, "theUser"));

String serialized = new ObjectMapper().writeValueAsString(myItem);

结果如下:

{

    "id": 1,

    "itemName": "theItem",

    "owner": {

        "id": 2,

        "name": "theUser"

    }

}

  • ObjectMapper 上的自定义序列化器

现在,让我们简化上面的 JSON 输出,只序列化 Userid,而不是整个 User 对象;我们希望得到如下简单的JSON:

{

    "id": 25,

    "itemName": "FEDUfRgS",

    "owner": 15

}

我们必须为 Item 对象定义一个自定义序列化器:

public class ItemSerializer extends JsonSerializer<Item> {

    @Override

    public void serialize(Item value, JsonGenerator jgen, SerializerProvider provider)

      throws IOException, JsonProcessingException {

        jgen.writeStartObject();

        jgen.writeNumberField("id", value.id);

        jgen.writeStringField("itemName", value.itemName);

        jgen.writeNumberField("owner", value.owner.id);

        jgen.writeEndObject();

    }

}

现在,我们需要为 Item 类在 ObjectMapper 注册这个自定义序列化器,然后,执行序列化:

Item myItem = new Item(1, "theItem", new User(2, "theUser"));

ObjectMapper mapper = new ObjectMapper();

 

SimpleModule module = new SimpleModule();

module.addSerializer(Item.class, new ItemSerializer());

mapper.registerModule(module);

 

String serialized = mapper.writeValueAsString(myItem);

  • 在类上自定义序列化器

我们也可以在类上直接注册序列化器,而不用在 ObjectMapper

@JsonSerialize(using = ItemSerializer.class)

public class Item {

    ...

}

现在,当我们执行标准的序列化时:

Item myItem = new Item(1, "theItem", new User(2, "theUser"));

String serialized = new ObjectMapper().writeValueAsString(myItem);

我们会得到自定义的 JSON 输出,它是由 @JsonSerialize 指定的序列化器创建的:

{

    "id": 25,

    "itemName": "FEDUfRgS",

    "owner": 15

}

 

高级 Jackson Unmarshalling


Unmarshall 成 Collection/Array

如何用 Jackson 2 把一个 JSON 数组(Array )反序列化成一个 Java 数组或集合(Collection )。

  • Unmarshall 成数组(Array)

Jackson 可以很容易地反序列化成一个 Java 数组:

@Test

public void givenJsonArray_whenDeserializingAsArray_thenCorrect() 

  throws JsonParseException, JsonMappingException, IOException {

    ObjectMapper mapper = new ObjectMapper();

    List<MyDto> listOfDtos = Lists.newArrayList(

      new MyDto("a", 1, true), new MyDto("bc", 3, false));

    String jsonArray = mapper.writeValueAsString(listOfDtos);

    // [{"stringValue":"a","intValue":1,"booleanValue":true},

    // {"stringValue":"bc","intValue":3,"booleanValue":false}]

 

    MyDto[] asArray = mapper.readValue(jsonArray, MyDto[].class);

    assertThat(asArray[0], instanceOf(MyDto.class));

}

  • Unmarshall 成集合(Collection)

把同一个 JSON 数组读到一个 Java 集合,就有点困难了 – 默认情况,Jackson 无法得到完整的泛型类型信息,而是将创建一个 LinkedHashMap 实例的集合:

@Test

public void givenJsonArray_whenDeserializingAsListWithNoTypeInfo_thenNotCorrect() 

  throws JsonParseException, JsonMappingException, IOException {

    ObjectMapper mapper = new ObjectMapper();

 

    List<MyDto> listOfDtos = Lists.newArrayList(

      new MyDto("a", 1, true), new MyDto("bc", 3, false));

    String jsonArray = mapper.writeValueAsString(listOfDtos);

 

    List<MyDto> asList = mapper.readValue(jsonArray, List.class);

    assertThat((Object) asList.get(0), instanceOf(LinkedHashMap.class));

}

有两种方式可以帮助 Jackson 理解正确的类型信息 – 我们或是使用 TypeReference

@Test

public void givenJsonArray_whenDeserializingAsListWithTypeReferenceHelp_thenCorrect() 

  throws JsonParseException, JsonMappingException, IOException {

    ObjectMapper mapper = new ObjectMapper();

 

    List<MyDto> listOfDtos = Lists.newArrayList(

      new MyDto("a", 1, true), new MyDto("bc", 3, false));

    String jsonArray = mapper.writeValueAsString(listOfDtos);

 

    List<MyDto> asList = mapper.readValue(jsonArray, new TypeReference<List<MyDto>>() { });

    assertThat(asList.get(0), instanceOf(MyDto.class));

}

或是使用接受 JavaTypereadValue 的重载方法:

@Test

public final void givenJsonArray_whenDeserializingAsListWithJavaTypeHelp_thenCorrect() 

  throws JsonParseException, JsonMappingException, IOException {

    ObjectMapper mapper = new ObjectMapper();

 

    List<MyDto> listOfDtos = Lists.newArrayList(

      new MyDto("a", 1, true), new MyDto("bc", 3, false));

    String jsonArray = mapper.writeValueAsString(listOfDtos);

 

    final CollectionType javaType = 

      mapper.getTypeFactory().constructCollectionType(List.class, MyDto.class);

    List<MyDto> asList = mapper.readValue(jsonArray, javaType);

    assertThat(asList.get(0), instanceOf(MyDto.class));

}

最后,需要注意的是,MyDto 类需要一个没有任何参数的默认构造函数,否则,Jackson 将不能实例化:

com.fasterxml.jackson.databind.JsonMappingException: 

No suitable constructor found for type [simple type, class org.baeldung.jackson.ignore.MyDto]: 

can not instantiate from JSON object (need to add/enable type information?)

Jackson – 自定义反序列化器

如何用 Jackson 2 通过一个自定义反序列化器来反序列化 JSON。

标准的反序列化

定义两个实体,如何用 Jackson 2 反序列化成一个 JSON 表示,不用任何的自定义:

public class User {

    public int id;

    public String name;

}

public class Item {

    public int id;

    public String itemName;

    public User owner;

}

现在,我们定义反序列化后想要得到的 JSON 表示:

{

    "id": 1,

    "itemName": "theItem",

    "owner": {

        "id": 2,

        "name": "theUser"

    }

}

最后,把这个 JSON unmarshall 成 Java 实体:

Item itemWithOwner = new ObjectMapper().readValue(json, Item.class);

在 ObjectMapper 上自定义反序列化器

在前面的示例中,JSON 表示与 Java 实体完全匹配。下面,我们简化 JSON:

{

    "id": 1,

    "itemName": "theItem",

    "createdBy": 2

}

当把这个 JSON unmarshalling 成相同的实体时,默认情况,将会失败:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:

Unrecognized field "createdBy" (class org.baeldung.jackson.dtos.Item),

not marked as ignorable (3 known properties: "id", "owner", "itemName"])

 at [Source: java.io.StringReader@53c7a917; line: 1, column: 43]

 (through reference chain: org.baeldung.jackson.dtos.Item["createdBy"])

现在,我们用自定义的反序列化器来反序列化:

public class ItemDeserializer extends JsonDeserializer<Item> {

 

    @Override

    public Item deserialize(JsonParser jp, DeserializationContext ctxt)

      throws IOException, JsonProcessingException {

        JsonNode node = jp.getCodec().readTree(jp);

        int id = (Integer) ((IntNode) node.get("id")).numberValue();

        String itemName = node.get("itemName").asText();

        int userId = (Integer) ((IntNode) node.get("createdBy")).numberValue();

 

        return new Item(id, itemName, new User(userId, null));

    }

}

反序列化使用 JSON 的标准 Jackson 表示 – JsonNode。一旦输入的 JSON 被表示成一个 JsonNode,就可以从它提取相关的信息,并构造自己的 Item 实体。

我们需要注册这个自定义的反序列化器,再正常地反序列化 JSON:

ObjectMapper mapper = new ObjectMapper();

SimpleModule module = new SimpleModule();

module.addDeserializer(Item.class, new ItemDeserializer());

mapper.registerModule(module);

 

Item readValue = mapper.readValue(json, Item.class);

在类的级别上自定义反序列化器

另一个方法是,也可以直接在类上注册反序列化器:

@JsonDeserialize(using = ItemDeserializer.class)

public class Item {

    ...

}

用这个定义在类级别上的反序列化器,就不需要在 ObjectMapper 注册它,那么一个默认的 mapper 就可以反序列化:

Item itemWithOwner = new ObjectMapper().readValue(json, Item.class);

 

演示


图 1 项目结构

  • libs 里,除了 jackson 2 的三个包外,还有 google guava 的一个包,是 Java 项目广泛依赖的核心库,如集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等。

 

参考资料


 

下载 Demo

下载 Jackson 2

Android 高级 Jackson Marshalling(serialize)/Unmarshalling(deserialize)的更多相关文章

  1. Android 基本 Jackson Marshalling(serialize)/Unmarshalling(deserialize)

    本文内容 基本 Jack Marshalling 忽略属性 忽略 Null 字段 改变字段名字 基本 Jackson Marshalling 把 JSON 解析成 JsonNode Unmarshal ...

  2. [LeetCode] Serialize and Deserialize BST 二叉搜索树的序列化和去序列化

    Serialization is the process of converting a data structure or object into a sequence of bits so tha ...

  3. [LeetCode] Serialize and Deserialize Binary Tree 二叉树的序列化和去序列化

    Serialization is the process of converting a data structure or object into a sequence of bits so tha ...

  4. U家面试prepare: Serialize and Deserialize Tree With Uncertain Children Nodes

    Like Leetcode 297, Serialize and Deserialize Binary Tree, the only difference, this is not a binary ...

  5. Leetcode: Serialize and Deserialize BST

    Serialization is the process of converting a data structure or object into a sequence of bits so tha ...

  6. [LeetCode] Serialize and Deserialize Binary Tree

    Serialize and Deserialize Binary Tree Serialization is the process of converting a data structure or ...

  7. LeetCode——Serialize and Deserialize Binary Tree

    Description: Serialization is the process of converting a data structure or object into a sequence o ...

  8. android.os.BadParcelableException: ClassNotFoundException when unmarshalling:解决办法

    例如在用AlarmManager的时候 AlarmManager alarmMgr = (AlarmManager) mContext .getSystemService(Context.ALARM_ ...

  9. Serialize and Deserialize Binary Tree

    Design an algorithm and write code to serialize and deserialize a binary tree. Writing the tree to a ...

随机推荐

  1. linux 内核升级2 转

    linux内核升级 一.Linux内核概览 Linux是一个一体化内核(monolithic kernel)系统. 设备驱动程序可以完全访问硬件. Linux内的设备驱动程序可以方便地以模块化(mod ...

  2. TextAppearance.Material.Widget.Button.Inverse,Widget.Material.Button.Colored

    编译xamarin android项目报错: android:TextAppearance.Material.Widget.Button.Inverse android:Widget.Material ...

  3. Noip2005谁拿了最多的奖学金题解

    题解 题目本身没什么好说的. 只是一道普及组的题让我领悟到scanf()读字符的真谛.scanf()函数最奇异的功能就是控制串里除格式化字符串之外的字符.若匹配成功则舍去. 所以我们能够"精 ...

  4. spring-boot项目在eclipse中指定配置文件启动

    原文:https://blog.csdn.net/ztx114/article/details/80076339 如下图我的项目有三个配置文件,假如我向指定用application-test.yml启 ...

  5. pthread_join与pthread_detach细节问题

    http://www.360doc.com/content/13/0106/09/9171956_258497083.shtml pthread_t    pthr; pthread_create(& ...

  6. rocketmq 管理控制台

    docker pull styletang/rocketmq-console-ng    docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=1 ...

  7. Nvidia驱动正确安装过程

    找到适合的正确的驱动 去nvidia驱动官网下载 卸载掉原有驱动 sudo apt-get remove –purge nvidia* 安装驱动 进入命令行界面 Ctrl-Alt+F1 给驱动run文 ...

  8. Svg.js 图片加载

    一.SVG.Image 1.创建和修改图片 var draw = SVG('svg1').size(300, 300); //SVG.Image 加载图片文件 var image = draw.ima ...

  9. Ubuntu下搭建Hyperledger Fabric v1.0环境

      多次尝试才正常启动了Fabric,如遇到各种莫名错误,请参考如下一步步严格安装,特别用户权限需要注意. 一.安装Ubuntu16 虚拟机或双系统,虚拟机有VirtualBox或者VMware,Ub ...

  10. maven与jdk版本不一致报:Unsupported major.minor version 51.0

    I recently uninstalled Java 8, to use Java 6 as I want my code/creations to be usable by more people ...