成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

XStream自定義XML轉(zhuǎn)換器

Little_XM / 3507人閱讀

摘要:跟進(jìn)解析的源碼,沒找到加載的地方,時(shí)間緊迫,也沒時(shí)間去仔細(xì)閱讀文檔,于是干脆自己動(dòng)手重寫了一個(gè)簡單的從到的轉(zhuǎn)換器。自定義直接實(shí)現(xiàn)這個(gè)接口,方法返回,直接接手整個(gè)的解析工作。

莫名其妙的異常

昨天做一個(gè)項(xiàng)目時(shí)用到了XStream來做XML到Bean的轉(zhuǎn)換器,需要轉(zhuǎn)換的Bean格式如下:

@Data
@XStreamAlias("Document")
public class AccountTradeHistoryResponseVo {

    @XStreamAlias("ResponseHeader")
    private CommonResponseHeader header;

    @XStreamAlias("Content")
    private List content;

}

本以為一切順利,結(jié)果卻報(bào)了個(gè)意料之外的異常:

java.lang.ClassCastException: com.xinzhen.pay.vo.jj.powercore.response.AccountTradeHistoryDetail cannot be cast to com.xinzhen.pay.vo.jj.powercore.response.AccountTradeHistoryDetail

明明是同一個(gè)類,怎么就轉(zhuǎn)換異常了呢,百思不得其解!

Converter鏈

XStream提供了Converter接口可以用來自定義轉(zhuǎn)換器,接口定義如下:

public interface Converter extends ConverterMatcher {

    // Bean -> XML/Json
    void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context);

    // XML/Json -> Bean
    Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context);
}

public interface ConverterMatcher {

    // 是否支持clazz類型的轉(zhuǎn)換
    boolean canConvert(Class clazz);
    
}

Converter的設(shè)計(jì)使用了責(zé)任鏈模式,類似于SpringMVC的ViewResolvers鏈,通過canConverter()方法判斷是否支持該元素類型的轉(zhuǎn)換,如果支持則調(diào)用這個(gè)Converter的marshal()或unmarshal()來做Bean到XML/Json之間的轉(zhuǎn)換;否則轉(zhuǎn)移到下一個(gè)注冊的Converter繼續(xù)判斷流程。

先簡單繼承了一下AbstractCollectionConverter,然后在解析的時(shí)候注冊這個(gè)Converter,查看一下這里的Class之間到底有什么貓膩。

public class CustomCollectionConverter extends AbstractCollectionConverter {

    public CustomCollectionConverter(Mapper mapper) {
        super(mapper);
    }

    @Override
    public boolean canConvert(Class clazz) {
        Class clazz1 = AccountTradeHistoryDetail.class;
        System.out.println(clazz1 == clazz);
        ClassLoader classLoader1 = clazz.getClassLoader();
        ClassLoader classLoader2 = clazz1.getClassLoader();
        return clazz1 == clazz;
    }

    @Override
    public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) {

    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        return null;
    }
}

果然不出所料,當(dāng)傳進(jìn)來的clazz是AccountTradeHistoryDetail.class時(shí),跟clazz1竟然不是同一個(gè)Class對象,兩個(gè)ClassLoader也不相同,一個(gè)是RestartClassLoader, 另一個(gè)是AppClassLoader;因?yàn)轫?xiàng)目是使用SpringBoot構(gòu)建的,有兩個(gè)ClassLoader是正常的,但為什么AccountTradeHistoryDetail.class這個(gè)類會(huì)被這兩個(gè)ClassLoader分別加載一次呢?為了排除SpringBoot本身的問題,于是又寫了個(gè)方法測試了一下:

Class clazz = AccountTradeHistoryDetail.class;
Field f = AccountTradeHistoryResponseVo.class.getDeclaredField("content");
// content為List,很明顯是泛型參數(shù)
ParameterizedType t = (ParameterizedType) f.getGenericType();
Type[] types = t.getActualTypeArguments();
Class clazz1 = (Class) types[0]; // 第一個(gè)類型就是實(shí)際泛型類型
System.out.println(clazz == clazz1);

這個(gè)地方為true,說明在這里這兩個(gè)AccountTradeHistoryDetail是同一個(gè)Class對象,那么就可以排除SpringBoot的問題;看來是XStream出于什么原因重新加載了這個(gè)類,但是明明可以通過反射從字段中得出實(shí)際的參數(shù)類型,不知道XStream為什么要這么做。跟進(jìn)XStream解析的源碼,沒找到加載Class的地方,時(shí)間緊迫,也沒時(shí)間去仔細(xì)閱讀文檔,于是干脆自己動(dòng)手重寫了一個(gè)簡單的從XML到Bean的轉(zhuǎn)換器。

自定義Converter

直接實(shí)現(xiàn)Converter這個(gè)接口,canConvert()方法返回true,直接接手整個(gè)Document的解析工作。

public class CustomConverter implements Converter {

    // 根結(jié)點(diǎn)下的成員變量類型
    private Map rootTypeMap;

    // 根結(jié)點(diǎn)下List成員類型(若泛型 T=List, 也應(yīng)該放在listItemType里)
    private Map listItemMap;

    // 根結(jié)點(diǎn)下的成員變量字段
    private Map rootFieldMap;

    // 要解析的類型實(shí)例(ROOT)
    private Object instance;

    /**
     * @param instanceType 要解析的實(shí)例類型
     * @param typeMap      泛型<成員變量名, 類型>Map
     * @param listItemType List類型<成員變量名, 類型>Map
     * @throws Exception
     */
    public CustomConverter(Class instanceType, Map typeMap, Map listItemType) throws Exception {
        instance = instanceType.newInstance();
        this.rootTypeMap = typeMap == null ? new HashMap<>() : rootTypeMap;
        this.listItemMap = listItemType == null ? new HashMap<>() : listItemType;
        rootFieldMap = new HashMap<>();
        Field[] fields = instanceType.getDeclaredFields();
        for (Field field : fields) {
            XStreamAlias annotation = field.getAnnotation(XStreamAlias.class);
            // 字段名, 如果設(shè)置了別名則使用別名
            String fieldName = annotation == null ? field.getName() : annotation.value();
            rootFieldMap.put(fieldName, field);
        }
    }

    @Override
    public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) {

    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        try {
            // Root下節(jié)點(diǎn)處理
            while (reader.hasMoreChildren()) {
                reader.moveDown();
                String nodeName = reader.getNodeName();
                Field field = rootFieldMap.get(nodeName);
                if (field == null) {
                    reader.moveUp();
                    continue;
                }
                Class type = rootTypeMap.get(nodeName);
                if (type == null) {
                    type = field.getType();
                }
                field.setAccessible(true);
                // 該節(jié)點(diǎn)為List類型
                if (listItemMap.containsKey(nodeName)) {
                    List list = new ArrayList();
                    Class itemType = listItemMap.get(nodeName);
                    if (itemType == String.class) { // List
                        while (reader.hasMoreChildren()) {
                            reader.moveDown();
                            list.add(reader.getValue());
                            reader.moveUp();
                        }
                    } else { // List
                        while (reader.hasMoreChildren()) {
                            reader.moveDown();
                            list.add(parseObject(itemType, reader));
                            reader.moveUp();
                        }
                    }
                    field.set(instance, list);
                } else if (type == String.class) { // 該節(jié)點(diǎn)為String類型, 直接設(shè)置value
                    field.set(instance, reader.getValue());
                } else { // 非String類型, 解析該節(jié)點(diǎn)
                    field.set(instance, parseObject(type, reader));
                }
                reader.moveUp();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return instance;
    }

    /**
     * 解析子節(jié)點(diǎn): 子節(jié)點(diǎn)只能是非基本類型(包括String)
     *
     * @param type
     * @param reader
     * @return
     */
    public Object parseObject(Class type, HierarchicalStreamReader reader) throws Exception {
        Object obj = type.newInstance();
        Map fieldMap = new HashMap<>();
        Field[] fields = type.getDeclaredFields();
        for (Field field : fields) {
            XStreamAlias annotation = field.getAnnotation(XStreamAlias.class);
            // 字段名, 如果設(shè)置了別名則使用別名
            String fieldName = annotation == null ? field.getName() : annotation.value();
            fieldMap.put(fieldName, field);
        }
        while (reader.hasMoreChildren()) {
            reader.moveDown();
            String nodeName = reader.getNodeName();
            // 獲取對應(yīng)的字段
            Field field = fieldMap.get(nodeName);
            if (field == null) {
                reader.moveUp();
                continue;
            }
            Class fType = field.getType();
            field.setAccessible(true);
            if (fType == String.class) { // String類型, 直接設(shè)置value
                field.set(obj, reader.getValue());
            } else { // 其他類型, 繼續(xù)解析
                field.set(obj, parseObject(fType, reader));
            }
            reader.moveUp();
        }
        return obj;
    }

    /**
     * 這個(gè)Converter作為所有字段的Converter
     *
     * @param type
     * @return
     */
    @Override
    public boolean canConvert(Class type) {
        return true;
    }

}

該注冊器的構(gòu)造方法有幾個(gè)關(guān)鍵參數(shù):

Class instanceType: 要轉(zhuǎn)換的目標(biāo)類型

Map typeMap: 泛型類型的字段名和實(shí)際類型的Map

Map listItemType: List類型的字段名和實(shí)際類型的Map

雖然功能簡單,但至少滿足了目前轉(zhuǎn)換的需求;有關(guān)XStream類加載的問題,有時(shí)間還得好好研究。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68107.html

相關(guān)文章

  • XStream定義XML轉(zhuǎn)換器

    摘要:跟進(jìn)解析的源碼,沒找到加載的地方,時(shí)間緊迫,也沒時(shí)間去仔細(xì)閱讀文檔,于是干脆自己動(dòng)手重寫了一個(gè)簡單的從到的轉(zhuǎn)換器。自定義直接實(shí)現(xiàn)這個(gè)接口,方法返回,直接接手整個(gè)的解析工作。 莫名其妙的異常 昨天做一個(gè)項(xiàng)目時(shí)用到了XStream來做XML到Bean的轉(zhuǎn)換器,需要轉(zhuǎn)換的Bean格式如下: @Data @XStreamAlias(Document) public class AccountT...

    Nosee 評論0 收藏0
  • 使用XStream實(shí)現(xiàn)Java對象與XML互相轉(zhuǎn)換

    摘要:簡介是一個(gè)對象與互相轉(zhuǎn)換的工具類庫。官網(wǎng)鏈接簡單使用下載頁面使用構(gòu)建項(xiàng)目的加入以下依賴創(chuàng)建對象轉(zhuǎn)使用方法。創(chuàng)建解析對象設(shè)置別名默認(rèn)會(huì)輸出全路徑轉(zhuǎn)為轉(zhuǎn)換后的文本為轉(zhuǎn)對象使用方法。 XStream簡介 XStream是一個(gè)Java對象與XML互相轉(zhuǎn)換的工具類庫。 官網(wǎng)鏈接: http://x-stream.github.io/index.html 簡單使用 下載頁面:http://x-st...

    崔曉明 評論0 收藏0
  • 2016年度最受歡迎的100個(gè) Java 庫

    摘要:最受歡迎的個(gè)庫連續(xù)兩年,二度成為中最受歡迎的庫。此外,谷歌的開源項(xiàng)目來勢洶洶,勇奪第三名,該庫包含了一系列谷歌內(nèi)含的核心庫。在本次最受歡迎的個(gè)庫中,個(gè)庫與相關(guān)。 【編者按】本文作者為 Henn Idan,主要介紹基于 GitHub 中的數(shù)據(jù)分析,得出的2016年度最受歡迎的100個(gè) Java 庫。本文系國內(nèi) ITOM 管理平臺 OneAPM 編譯呈現(xiàn)。 誰拔得頭籌?誰又落于人后?我們分...

    nihao 評論0 收藏0
  • java版微信公眾號開發(fā)(四):定義菜單的實(shí)現(xiàn)

    摘要:想要實(shí)現(xiàn)自定義菜單的功能,需要有已認(rèn)證訂閱號和已認(rèn)證服務(wù)號。測試時(shí)可以嘗試取消關(guān)注公眾賬號后再次關(guān)注,則可以看到創(chuàng)建后的效果。 想要實(shí)現(xiàn)自定義菜單的功能,需要有已認(rèn)證訂閱號和已認(rèn)證服務(wù)號。對于測試開發(fā)來說,可以直接申請一個(gè)測試賬號:http://mp.weixin.qq.com/debug... 同樣需要token的驗(yàn)證,前期接口已經(jīng)定義好了,直接拿來就可以 showImg(https...

    mo0n1andin 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<