Jackson--protobuf序列化积累

一 JACKSON 序列化相关


1  JACKSON序列化问题解决

       最近使用了jackson json来格式化数据输出,但是反序列化生成对象的时候碰到点麻烦,jackson把数据默认解析成了Map对象,

  经查询文档,问题解决,在ObjectMapper的readvalue方法中按Object所使用的类型声明即可,代码如下:

Map<Integer, RbtCounter> srcMap = new LinkedHashMap();

Map<Integer, RbtCounter> destMap;


String jsonData = mapper.writeValueAsString(srcMap);


正确:   

destMap = mapper.readValue(jsonData, new TypeReference<Map<Integer, RbtCounter>>(){});   // 反序列化

TypeReference -- 让Jackson Json在List/Map中识别自己的Object


错误

destMap = mapper.readValue(jsonData, LinkedHashMap.class);     List中的自定义Object同理解决。

2 未知属性异常

在使用的过程中,很有可能会遇到json反序列化的问题。当你对象中有get***()的地方,它就当做它是一个属性,所以当你序列化json之后,在反序列化的时候,很有可能会出现异常的情况,因为在你的model中没有这个***的定义。

那该如何处理和解决呢?

jackson给出了它自己的解决方案(JacksonHowToIgnoreUnknow):

1. 在class上添加忽略未知属性的声明:@JsonIgnoreProperties(ignoreUnknown=true)

2. 在反序列化中添加忽略未知属性解析,如下:

复制代码
 1 /**
 2  * json deserialize
 3  * @param json
 4  * @param mapClazz
 5  * @return
 6 */
 7 public static Object jsonDeserialize(final String json, final Class<?> mapClazz) {
 8     ObjectMapper om = new ObjectMapper();
 9     try {
10         // 忽略未知属性
11         om.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
12         return om.readValue(json, mapClazz);
13     } catch (Exception ex) {
14         return null;
15     }
16 }
复制代码

3. 添加"Any setter"来处理未知属性

复制代码
1 // note: name does not matter; never auto-detected, need to annotate
2 // (also note that formal argument type #1 must be "String"; second one is usually
3 // "Object", but can be something else -- as long as JSON can be bound to that type)
4 @JsonAnySetter
5 public void handleUnknown(String key, Object value) {
6     // do something: put to a Map; log a warning, whatever
7 }
复制代码

4. 注册问题处理句柄

注册一个DeserializationProblemHandler句柄,来调用ObjectMapper.addHandler()。当添加的时候,句柄的handleUnknownProperty方法可以在每一个未知属性上调用一次。

这个方法在你想要添加一个未知属性处理日志的时候非常有用:当属性不确定的时候,不会引起一个绑定属性的错误


demo 1

第一种:

Java code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public  class  JsonToJavaBean {
     public  static  void  main(String[] args) {
         String str= "{\"student\":[{\"name\":\"leilei\",\"age\":23},{\"name\":\"leilei02\",\"age\":23}]}" ;
         Student stu =  null ;
         List<Student> list =  null ;
         try  {
             ObjectMapper objectMapper= new  ObjectMapper();
             StudentList studentList=objectMapper.readValue(str, StudentList. class );
             
             list=studentList.getStudent();
         catch  (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
        
         for (Student s:list){
             System.out.println(s.getName()+ "   " +s.getAge());
         }
     }
}

第二种:
Java code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  static  void  main(String[] args) {
         ArrayList<Student> list= new  ArrayList<Student>();
         Student s1= new  Student();
         s1.setName( "leilei" );
         s1.setAge( 23 );
         Student s2= new  Student();
         s2.setName( "leilei02" );
         s2.setAge( 23 );
         list.add(s1);
         list.add(s2);
         
         StringWriter str= new  StringWriter();
         
         ObjectMapper objectMapper= new  ObjectMapper();
         try  {
             objectMapper.writeValue(str, list);
         catch  (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }   
         System.out.println(str);
     }

demo 2

第一种:
Java code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public  class  JsonToJavaBean {
     public  static  void  main(String[] args) {
         String str= "{\"student\":[{\"name\":\"leilei\",\"age\":23},{\"name\":\"leilei02\",\"age\":23}]}" ;
         Student stu =  null ;
         List<Student> list =  null ;
         try  {
             ObjectMapper objectMapper= new  ObjectMapper();
             StudentList studentList=objectMapper.readValue(str, StudentList. class );
             
             list=studentList.getStudent();
         catch  (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
        
         for (Student s:list){
             System.out.println(s.getName()+ "   " +s.getAge());
         }
     }
}

第二种:
Java code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  static  void  main(String[] args) {
         ArrayList<Student> list= new  ArrayList<Student>();
         Student s1= new  Student();
         s1.setName( "leilei" );
         s1.setAge( 23 );
         Student s2= new  Student();
         s2.setName( "leilei02" );
         s2.setAge( 23 );
         list.add(s1);
         list.add(s2);
         
         StringWriter str= new  StringWriter();
         
         ObjectMapper objectMapper= new  ObjectMapper();
         try  {
             objectMapper.writeValue(str, list);
         catch  (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }   
         System.out.println(str);
     }



student的这list包含了若干对象,每个对象都有各自的属性值的。 应该是你说的提出来的意思吧


2  基于protostuff的序列化


  • 传统方式

按照传统的开发流程,都是如下:


1.先编写proto文件格式,例如

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. message Person {  
  2.   required int32 id = 1;  
  3.   required string name = 2;  
  4.   optional string email = 3;  
  5. }  


2.运行编译程序,生成实体类Person.java
protoc  --java_out=./src   ./person.proto


3.在程序中可以直接使用Person类的相关函数进行序列化和反序列化

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //序列化  
  2. Person person = builder.build();  
  3. byte[] buf = person.toByteArray();  
  4. //反序列化  
  5. Person person2 = PersonProbuf.Person.parseFrom(buf);  

上面的流程看似很方便了,有什么问题呢?还能不能再改进呢?我们先看看生成的Person类代码吧,竟然有几千行。
实际上我们只是包含了3个变量而已,可读性大大降低了。其次,开发过程每次都得借助外部的编译工具来生成代码。


于是我们可能就有了一个简单的思路。自己编写Person的类,然后通过外部的api,直接对这个类进行序列化和反序列化。
直接抛弃了protoc工具。


这个思路,已经有人实现出来了,最早是在c#语言版本的protobuf-net实现了。它利用c#的Attribute(类似Java的Annotation注解)+反射来实现普通实体类的Protobuf序列化和
反序列化。


本想自己把protobuf-net的c#代码转换成Java代码。后来在OverStack中翻到有人介绍 Protostuff ,正是基于这种思路的。

  • 改进版
1.编写Person类(为了使demo代码精简,字段都暂时设为public)
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class Person{  
  2.     public int id;  
  3.     public String name;  
  4.     public String email;  
  5. }  

2.测试序列化和反序列化
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) throws IOException {  
  2.         // //类的模式设置为Person类  
  3.         Schema<Person> schema = RuntimeSchema.getSchema(Person.class);  
  4.         Person person1 = new Person();  
  5.         person1.id = 10086;  
  6.         person1.name = "ken";  
  7.         person1.email = "ken@iamcoding.com";  
  8.         // 缓存buff  
  9.         LinkedBuffer buffer = LinkedBuffer.allocate(1024);  
  10.         // 序列化成protobuf的二进制数据  
  11.         byte[] data = ProtobufIOUtil.toByteArray(person1, schema, buffer);  
  12.   
  13.   
  14.         // 反序列化  
  15.         Person person2 = new Person();  
  16.         ProtobufIOUtil.mergeFrom(data, person2, schema);  
  17.         System.out.println(person2.id);  
  18.     }  

更多的功能可以去详细阅读相关的手册吧。比如,指定实体类字段的序列化顺序,忽略某些字段不被序列化等等。

  • 性能考虑
一开始,考虑到Protostuff的这种反射,会不会比官方推荐的反射性能差很远呢?没有调查,就没有发言权。有人已经测试了,性能方面还是差不多的,可以放心使用。
具体的测试数据可以去查阅网上相关的数据,在此省略了。

  • 总结

Protostuff的优势是将开发流程简化,让我们可以更高效,更专注地开发。

参考:http://blog.csdn.net/janeky/article/details/17151465


版权声明:本文为wenzhibinbin_pt原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。