Fastjson API

ParseProcess是编程扩展定制反序列化的接口。fastjson支持如下ParseProcess:ExtraProcessor 用于处理多余的字段,ExtraTypeProvider 用于处理多余字段时提供类型信息。

ParseProcess

使用 ExtraProcessor 处理多余字段

public static class VO {
    private int id;
    private Map<String, Object> attributes = new HashMap<String, Object>();
    public int getId() { return id; }
    public void setId(int id) { this.id = id;}
    public Map<String, Object> getAttributes() { return attributes;}
}
ExtraProcessor processor = new ExtraProcessor() {
    public void processExtra(Object object, String key, Object value) {
        VO vo = (VO) object;
        vo.getAttributes().put(key, value);
    }
};
VO vo = JSON.parseObject("{"id":123,"name":"abc"}", VO.class, processor);
Assert.assertEquals(123, vo.getId());
Assert.assertEquals("abc", vo.getAttributes().get("name"));

使用 ExtraTypeProvider 为多余的字段提供类型

public static class VO {
    private int id;
    private Map<String, Object> attributes = new HashMap<String, Object>();
    public int getId() { return id; }
    public void setId(int id) { this.id = id;}
    public Map<String, Object> getAttributes() { return attributes;}
}
class MyExtraProcessor implements ExtraProcessor, ExtraTypeProvider {
    public void processExtra(Object object, String key, Object value) {
        VO vo = (VO) object;
        vo.getAttributes().put(key, value);
    }
    public Type getExtraType(Object object, String key) {
        if ("value".equals(key)) {
            return int.class;
        }
        return null;
    }
};
ExtraProcessor processor = new MyExtraProcessor();
VO vo = JSON.parseObject("{"id":123,"value":"123456"}", VO.class, processor);
Assert.assertEquals(123, vo.getId());
Assert.assertEquals(123456, vo.getAttributes().get("value")); // value本应该是字符串类型的,通过getExtraType的处理变成Integer类型了。

SerializeFilter

SerializeFilter是通过编程扩展的方式定制序列化。fastjson支持6种SerializeFilter,用于不同场景的定制序列化。

  1. PropertyPreFilter 根据PropertyName判断是否序列化
  2. PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
  3. NameFilter 修改Key,如果需要修改Key,process返回值则可
  4. ValueFilter 修改Value
  5. BeforeFilter 序列化时在最前添加内容
  6. AfterFilter 序列化时在最后添加内容

PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化

  • public interface PropertyFilter extends SerializeFilter {
          boolean apply(Object object, String propertyName, Object propertyValue);
      }
    
  • 可以通过扩展实现根据object或者属性名称或者属性值进行判断是否需要序列化。例如:
    PropertyFilter filter = new PropertyFilter() {
        public boolean apply(Object source, String name, Object value) {
            if ("id".equals(name)) {
                int id = ((Integer) value).intValue();
                return id >= 100;
            }
            return false;
        }
    };
        JSON.toJSONString(obj, filter); // 序列化的时候传入filter
    

PropertyPreFilter 根据PropertyName判断是否序列化

  • 和PropertyFilter不同只根据object和name进行判断,在调用getter之前,这样避免了getter调用可能存在的异常。
    public interface PropertyPreFilter extends SerializeFilter {
        boolean apply(JSONSerializer serializer, Object object, String name);
    }
    

NameFilter 序列化时修改Key

  • 如果需要修改Key,process返回值则可
    public interface NameFilter extends SerializeFilter {
        String process(Object object, String propertyName, Object propertyValue);
    }
    
  • fastjson内置一个PascalNameFilter,用于输出将首字符大写的Pascal风格。 例如:
    import com.alibaba.fastjson.serializer.PascalNameFilter;
    Object obj = ...;
    String jsonStr = JSON.toJSONString(obj, new PascalNameFilter());
    

ValueFilter 序列化是修改Value

public interface ValueFilter extends SerializeFilter {
    Object process(Object object, String propertyName, Object propertyValue);
}

BeforeFilter 序列化时在最前添加内容

  • 在序列化对象的所有属性之前执行某些操作,例如调用 writeKeyValue 添加内容
    public abstract class BeforeFilter implements SerializeFilter {
        protected final void writeKeyValue(String key, Object value) { ... }
        // 需要实现的抽象方法,在实现中调用writeKeyValue添加内容
        public abstract void writeBefore(Object object);
    }
    

AfterFilter 序列化时在最后添加内容

  • 在序列化对象的所有属性之后执行某些操作,例如调用 writeKeyValue 添加内容
    public abstract class AfterFilter implements SerializeFilter {
        protected final void writeKeyValue(String key, Object value) { ... }
        // 需要实现的抽象方法,在实现中调用writeKeyValue添加内容
        public abstract void writeAfter(Object object);
    }
    

BeanToArray

在fastjson中,支持一种叫做BeanToArray的映射模式。普通模式下,JavaBean映射成JSON object,BeanToArray模式映射为JSON array。

  • Sample 1
    class Mode {
       public int id;
       public int name;
    }
    Model model = new Model();
    model.id = 1001;
    model.name = "gaotie";
    // {"id":1001,"name":"gaotie"}
    String text_normal = JSON.toJSONString(model); 
    // [1001,"gaotie"]
    String text_beanToArray = JSON.toJSONString(model, SerializerFeature.BeanToArray); 
    // support beanToArray & normal mode
    JSON.parseObject(text_beanToArray, Feature.SupportArrayToBean);
    
    • 上面的例子中,BeanToArray模式下,少了Key的输出,节省了空间,JSON字符串较小,性能也会更好。
  • Sample 2
    • BeanToArray可以局部使用,比如:
      class Company {
           public int code;
           public List<Department> departments = new ArrayList<Department>();
      }
      @JSONType(serialzeFeatures=SerializerFeature.BeanToArray, parseFeatures=Feature.SupportArrayToBean)
      class Department {
           public int id;
           public Stirng name;
           public Department() {}
           public Department(int id, String name) {this.id = id; this.name = name;}
      }
      Company company = new Company();
      company.code = 100;
      company.departments.add(new Department(1001, "Sales"));
      company.departments.add(new Department(1002, "Financial"));
      // {"code":10,"departments":[[1001,"Sales"],[1002,"Financial"]]}
      String text = JSON.toJSONString(commpany);
      
    • 在这个例子中,如果Company的属性departments元素很多,局部采用BeanToArray就可以获得很好的性能,而整体又能够获得较好的可读性。
  • Sample 3
    • 上一个例子也可以这样写:
      class Company {
           public int code;
           @JSONField(serialzeFeatures=SerializerFeature.BeanToArray, parseFeatures=Feature.SupportArrayToBean)
           public List<Department> departments = new ArrayList<Department>();
      }
      

性能

  • 使用BeanToArray模式,可以获得媲美Protobuf的性能。
    create     ser   deser   total   size  +dfl
    protobuf                              244    2297    1296    3593    239   149
    json/fastjson_array/databind          123    1289    1567    2856    281   163
    msgpack/databind                      122    1525    2180    3705    233   146
    json/fastjson/databind                120    2019    2610    4629    486   262
    json/jackson+afterburner/databind     118    2142    3147    5289    485   261
    json/jackson/databind                 124    2914    4411    7326    485   261
    
  • 这里的json/fastjson_array/databind就是fastjson启用BeanToArray模式,total性能比Protobuf好,请看fastjson Benchmark
下一节:在日志解析,前后端数据传输交互中,经常会遇到 String 与 map、json、xml 等格式相互转换与解析的场景,其中 json 基本成为了跨语言、跨前后端的事实上的标准数据交互格式。应该来说各个语言中 解析 json 的库都一大片(具体 json 格式与三方库的介绍请见: http://www.json.org/json-zh.html ),比如 python 都集成在了内置库中,成为标准 API,今天我们要聊的是 java 中如何方便的使用 json 格式。