国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

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

Nosee / 1766人閱讀

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

莫名其妙的異常

昨天做一個(gè)項(xiàng)目時(shí)用到了XStream來(lái)做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è)類(lèi),怎么就轉(zhuǎn)換異常了呢,百思不得其解!

Converter鏈

XStream提供了Converter接口可以用來(lái)自定義轉(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類(lèi)型的轉(zhuǎn)換
    boolean canConvert(Class clazz);
    
}

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

先簡(jiǎn)單繼承了一下AbstractCollectionConverter,然后在解析的時(shí)候注冊(cè)這個(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)來(lái)的clazz是AccountTradeHistoryDetail.class時(shí),跟clazz1竟然不是同一個(gè)Class對(duì)象,兩個(gè)ClassLoader也不相同,一個(gè)是RestartClassLoader, 另一個(gè)是AppClassLoader;因?yàn)轫?xiàng)目是使用SpringBoot構(gòu)建的,有兩個(gè)ClassLoader是正常的,但為什么AccountTradeHistoryDetail.class這個(gè)類(lèi)會(huì)被這兩個(gè)ClassLoader分別加載一次呢?為了排除SpringBoot本身的問(wèn)題,于是又寫(xiě)了個(gè)方法測(cè)試了一下:

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

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

自定義Converter

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

public class CustomConverter implements Converter {

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

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

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

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

    /**
     * @param instanceType 要解析的實(shí)例類(lèi)型
     * @param typeMap      泛型<成員變量名, 類(lèi)型>Map
     * @param listItemType List類(lèi)型<成員變量名, 類(lèi)型>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)為L(zhǎng)ist類(lèi)型
                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類(lèi)型, 直接設(shè)置value
                    field.set(instance, reader.getValue());
                } else { // 非String類(lèi)型, 解析該節(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)只能是非基本類(lèi)型(包括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();
            // 獲取對(duì)應(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類(lèi)型, 直接設(shè)置value
                field.set(obj, reader.getValue());
            } else { // 其他類(lèi)型, 繼續(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;
    }

}

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

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

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

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/110369.html

相關(guān)文章

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

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

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

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

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

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

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

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

    mo0n1andin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

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