Dozer物件對映框架Map到JSONString對映問題排查

panguanjing發表於2016-07-21

引言

Dozer是一個優秀的物件對映的框架,可以幫助程式設計師減少大量的物件之間對映的get/set程式碼,在ATA上有好幾篇文章介紹了dozer的使用:
dozer開發手冊
使用Dozer幫你提高開發效率(解決繁瑣的DO轉BO、TO轉BO問題) 有興趣的同學,可以去看下基本的使用。

問題

我在開發後臺系統中,經常會遇到從前臺提交的物件,轉為後臺的服務模型物件做操作,通過dozer工具,靈活的配置就可以輕易的解決。我需要將將一個物件的Map物件轉為json的字串,按照dozer的文件需要編寫自定義的Convertor來進行處理(這裡面針對欄位的轉換,,dozer支援類級別,欄位級別的Convertor. ),但測試卻發現了一些問題。具體請看例子:

源物件

     public class SourceA{
           private Map<String,Object> field;
           //省略get/set
     }

  public class SourceB{
           private String field;
           //省略get/set
    }
  //dozerMapper配置
  <?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">

    <mapping type="bi-directional"> 
        <class-a>com.alibaba.xxx.SourceA</class-a>
        <class-b>com.alibaba.xxx.SourceB</class-b>
       <field custom-converter="com.alibaba.xxx.JsonConverter">
            <a>field</a>
            <b>field</b>
        </field>
  </mapping>
</mappings>

//自定義的Convertor
public class JsonConverter extends DozerConverter<Map,String> {

    public JsonConverter(){
        super(Map.class, String.class);
    }
    @Override
    public String convertTo(Map source, String destination) {
        if (source == null) {
            return null;
        }
        return JSON.toJSONString(source);
    }
    @Override
    public Map convertFrom(String source, Map destination) {
        if (source == null) {
            return null;
        }
        return JSON.parseObject(source);
    }
}

 //單元測試:
 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:dozer_test/test.xml" })
public class DozerBugTest {

    @Resource
    DozerBeanMapper dozerBeanMapper;

    @Test
    public void test_mapper(){
        SourceA sa = new SourceA();
        Map<String,Object> map = Maps.newHashMap();
        map.put("losemoney", "losemoney");
        clueInfo.setField(map);
        SourceB sourceB = new SourceB();
        dozerBeanMapper.map(sa, sourceB);
        assertNotNull(sourceB.getField());
    }
}

從結果上,我是期望傳入的Map物件自動可以轉為JSON的字串進行處理,但是跑了單元測試執行的結果是不通過。

分析

我在自定義的轉換器JsonConverter類的convertTo方法中加了斷點,發現了傳入的源物件是一個空值:
screenshot
為什麼明明有map物件設定,到自定義轉換器哪裡就為空了。於是從dozer的map方法開始debug:

  1. 實際是呼叫了MappingProcessor做對映處理,
    screenshot
  2. 獲取每個欄位的值:screenshot 從斷點顯示的值看,這裡取到的就是null了。
  3. 問題就應該出在 srcFieldValue = fieldMapping.getSrcFieldValue(srcObj);
  4. FieldMap有多個物件實現類,screenshot,在這裡需要檢視MapFieldMap獲取物件。
    screenshot,預設設定Map和欄位做對映的時候,會將Map作為物件,呼叫get方法,以配置的欄位名作為key獲取值,在這個場景就是Map.get(“field”)取值,所以取到的為空。

解決方案

  1. 解決方案其實很簡單,更改配置對映檔案加上java.util.HashMap
  <?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">

    <mapping type="bi-directional"> 
        <class-a>com.alibaba.xxx.SourceA</class-a>
        <class-b>com.alibaba.xxx.SourceB</class-b>
       <field custom-converter="com.alibaba.xxx.JsonConverter">
            <a>field</a>
            <b>field</b>
           <a-hint>java.util.HashMap</a-hint>
        </field>
  </mapping>
</mappings>
  1. 重新執行後:
    screenshot

由於getSrcHintContainer() 不為空,會執行到標紅的邏輯,即直接從物件中獲取Map的欄位作為值返回,
screenshot


相關文章