ifeilong / feilong-core

:gem: Reduce development, Release ideas
Apache License 2.0
529 stars 155 forks source link

json format 需要支持修改key的名字 #505

Closed venusdrogon closed 8 years ago

venusdrogon commented 8 years ago

json format 需要支持修改key的名字

minglei 说, 我们的java 属性是标准的属性名字, 比如name 但是和第三方对接的时候, 对方的文档要求是 Name, 首字母需要大写的

所以需要增加这么一个功能

背景

我们这边的代码


public class CrmAddpointCommand implements Serializable{

    /** 用户编码 */
    private String openId;

    /** 渠道:Tmall - 天猫 JD - 京东 */
    private String consumptionChannel;

    /** 淘宝/京东买家账号 */
    private String buyerId;

    /** 电商订单编号 */
    private String orderCode;

  // setter getter

符合标准的java 代码规范

如果直接使用 com.feilong.tools.jsonlib.JsonUtil.format(Object)


    @Test
    public void testJsonTest(){
        CrmAddpointCommand crmAddpointCommand = new CrmAddpointCommand();

        crmAddpointCommand.setBuyerId("123456");
        crmAddpointCommand.setConsumptionChannel("feilongstore");
        crmAddpointCommand.setOpenId("feilong888888ky");
        crmAddpointCommand.setOrderCode("fl123456");

        LOGGER.debug(JsonUtil.format(crmAddpointCommand));
    }

输出结果

 {
        "orderCode": "fl123456",
        "buyerId": "123456",
        "consumptionChannel": "feilongstore",
        "openId": "feilong888888ky"
    }

输出出来的属性大小写和 crmAddpointCommand 对象里面字段的大小写相同

但是 对方接口要求

image

要求 首字符要大写

shade41-code commented 8 years ago
   JsonConfig jsonConfig = new JsonConfig();

    for (Class<?> class1 : classList){
        PropertyNameProcessor propertyNameProcessor = new PropertyNameProcessor(){
            @Override
            public String processPropertyName(Class target,String fieldName){
                return fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            }
        };
        jsonConfig.registerJsonPropertyNameProcessor(class1, propertyNameProcessor);
    }

    PropertyFilter propertyFilter = new PropertyFilter(){
        @Override
        public boolean apply(Object arg0,String arg1,Object arg2){
            return Validator.isNullOrEmpty(arg2);
        }
    };

    jsonConfig.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT);
    jsonConfig.setIgnoreDefaultExcludes(false);
    jsonConfig.registerJsonValueProcessor(Date.class, new DateJsonValueProcessor(DatePattern.COMMON_DATE_AND_TIME));
    jsonConfig.setAllowNonStringKeys(true);
    jsonConfig.setJsonPropertyFilter(propertyFilter);

    JSONObject.fromObject(obj, jsonConfig);

这段代码就可以将传入的对象每个属性首字母大写,而且属性值为空的属性不进行序列化

venusdrogon commented 8 years ago

先看看 http://blog.csdn.net/zhangzeyuaaa/article/details/49914761 示例 和 @shade41-code 代码相似

不过

我可能更期望 这种匹配关系由调用者来配置

venusdrogon commented 8 years ago

@shade41-code 首字符大写 可以使用 org.apache.commons.lang3.StringUtils.capitalize(String)

venusdrogon commented 8 years ago

做了一版本


    @Test
    public void testJsonTest(){
        CrmAddpointCommand crmAddpointCommand = new CrmAddpointCommand();

        crmAddpointCommand.setBuyerId("123456");
        crmAddpointCommand.setConsumptionChannel("feilongstore");
        crmAddpointCommand.setOpenId("feilong888888ky");
        crmAddpointCommand.setOrderCode("fl123456");

        //****************************************************************************************

        JsonFormatConfig jsonFormatConfig = new JsonFormatConfig();

        Map<Class<?>, PropertyNameProcessor> targetClassAndPropertyNameProcessorMap = newHashMap(1);
        targetClassAndPropertyNameProcessorMap.put(CrmAddpointCommand.class, CapitalizePropertyNameProcessor.INSTANCE);

        jsonFormatConfig.setJsonTargetClassAndPropertyNameProcessorMap(targetClassAndPropertyNameProcessorMap);

        //****************************************************************************************

        LOGGER.debug(JsonUtil.format(crmAddpointCommand, jsonFormatConfig));
    }

输出


{
        "OrderCode": "fl123456",
        "BuyerId": "123456",
        "ConsumptionChannel": "feilongstore",
        "OpenId": "feilong888888ky"
    }

原理

net.sf.json.JSONObject.defaultBeanProcessing(Object, JsonConfig) line 763


   private static JSONObject defaultBeanProcessing(Object bean, JsonConfig jsonConfig) {      
      Class beanClass = bean.getClass();
      PropertyNameProcessor propertyNameProcessor = jsonConfig.findJsonPropertyNameProcessor( beanClass );      
     .......
      JSONObject jsonObject = new JSONObject();
      try{
         PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors( bean );
         PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
         for( int i = 0; i < pds.length; i++ ){
            boolean bypass = false;
            String key = pds[i].getName();
            if( exclusions.contains( key ) ){
               continue;
            }

            if( jsonConfig.isIgnoreTransientFields() && isTransientField( key, beanClass, jsonConfig ) ){
               continue;
            }

            Class type = pds[i].getPropertyType();

            if( pds[i].getReadMethod() != null ){

               if(isTransient(pds[i].getReadMethod(), jsonConfig)) continue;

               Object value = PropertyUtils.getProperty( bean, key );
               if( jsonPropertyFilter != null && jsonPropertyFilter.apply( bean, key, value ) ){
                  continue;
               }
               JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(
                     beanClass, type, key );
               if( jsonValueProcessor != null ){
                  value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
                  bypass = true;
                  if( !JsonVerifier.isValidJsonValue( value ) ){
                     throw new JSONException( "Value is not a valid JSON value. " + value );
                  }
               }
               if( propertyNameProcessor != null ){
                  key = propertyNameProcessor.processPropertyName( beanClass, key );
               }
               setValue( jsonObject, key, value, type, jsonConfig, bypass );
....
         }

         try {
            if( !jsonConfig.isIgnorePublicFields() ) {
               Field[] fields = beanClass.getFields();
               for( int i = 0; i < fields.length; i++ ) {
                  JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor( beanClass, type, key );
                  if( jsonValueProcessor != null ) {
                     value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
                     bypass = true;
                     if( !JsonVerifier.isValidJsonValue( value ) ) {
                        throw new JSONException( "Value is not a valid JSON value. " + value );
                     }
                  }
                  if( propertyNameProcessor != null ) {
                     key = propertyNameProcessor.processPropertyName( beanClass, key );
                  }
              .....
               }
            }
         }

      return jsonObject;
   }
venusdrogon commented 8 years ago

不过感觉这么写代码量会比较大

可以参考

问题


   public PropertyNameProcessor findJsonPropertyNameProcessor( Class beanClass ) {
      if( !jsonPropertyNameProcessorMap.isEmpty() ) {
         Object key = jsonPropertyNameProcessorMatcher.getMatch( beanClass, jsonPropertyNameProcessorMap.keySet() );
         return (PropertyNameProcessor) jsonPropertyNameProcessorMap.get( key );

      }
      return null;
   }

不太适合使用特性, 如果要使用的话, 可能需要 重写 jsonPropertyNameProcessorMap