天天看點

關于xsteam @XStreamAlias 别名重複/沖突問題

xstream

<!-- xstream -->
    <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.4.11.1</version>
    </dependency>
           

xstream 反序列化

初始化與基本配置:

private static final XStream XSTREAM_TO_BEAN = new XStream(new DomDriver()) {
        @Override
        protected MapperWrapper wrapMapper(MapperWrapper next) {
            return new MapperWrapper(next) {
                @Override
                public boolean shouldSerializeMember(Class definedIn,
                                                     String fieldName) {
                    if (definedIn == Object.class) {
                        return false;
                    }
                    return super.shouldSerializeMember(definedIn, fieldName);
                }
            };
        }
    };

    /**
     * 初始化
     */
    private static final int INIT = init();

    private static int init() {
        xStreamConverter(XSTREAM_TO_BEAN);
        xStreamConverter(XSTREAM_TO_XML);
        return 0;
    }


    public static <T> void xStreamConverter(XStream xstream) {
        xstream.autodetectAnnotations(true);
        xstream.addPermission(AnyTypePermission.ANY);
        // spring boot 會導緻 ClassLoader 未正确配置
        xstream.setClassLoader(XmlUtils.class.getClassLoader());
        // 忽略多餘的xml節點
        xstream.ignoreUnknownElements();

        //下面是重寫轉換常用的類型
        xstream.registerConverter(...);
        xstream.registerConverter(...);
        xstream.registerConverter(...);
        ...
    }

           

反序列化方法:

/**
     * 将傳入xml文本轉換成Java對象
     *
     * @param xmlStr 字元串
     * @param cls    xml對應的cls
     * @param <T>    調用的方法執行個體:PersonBean person=XmlUtil.toBean(xmlStr, PersonBean.class);
     * @return bean
     */
      public static <T> T toBean(String xmlStr, Class<T> cls) {
		// 反序列化是需要手動processAnnotations(),auto是無效的
        XSTREAM_TO_BEAN.processAnnotations(cls);
        // xml解析過程中會将&作為開始,會把“&”當做一個實體引用的開始,而去尋找這個實體引用的結束符号“;"
        xmlStr = xmlStr.replace("&","&amp;");
        @SuppressWarnings("unchecked")
        T obj = (T) XSTREAM_TO_BEAN.fromXML(xmlStr);
        XSTREAM_TO_BEAN.fromXML()
        return obj;
    }
           

問題 1 : @XStreamAlias的第一種同名

近期在調用第三方的Web Service時,當同時具備多個類的别名相同時,如以下傳回listResponse,假設已經解析過EntitlementListDto,又有另一個xml需要解析,同樣是listResponse别名,但是是想拿到解析TestListDto,這個時候是xstream是根據listResponse拿到的是EntitlementListDto,會将後者的xml用EntitlementListDto去解析。

// 第一個類
@Data
@XStreamAlias(value = "listResponse")
public class EntitlementListDto { ... }

// 第二個類
@Data
@XStreamAlias(value = "listResponse")
public class TestListDto { ... }
           

解決方法:

最後通過看源碼,發現自己将對象作為參數時,xstream是直接拿到對象clss并對象操作,而不是通過别名,fromXml中就帶有這個參數。

/**
     * 将傳入xml文本轉換成Java對象
     *
     * @param xmlStr 字元串
     * @param cls    xml對應的cls
     * @param <T>    調用的方法執行個體:PersonBean person=XmlUtil.toBean(xmlStr, PersonBean.class);
     * @return bean
     */
    public static <T> T toBean(String xmlStr, Class<T> cls) {

        XSTREAM_TO_BEAN.processAnnotations(cls);
        // xml解析過程中會将&作為開始,會把“&”當做一個實體引用的開始,而去尋找這個實體引用的結束符号“;"
        xmlStr = xmlStr.replace("&", "&amp;");

        T obj = null;
        try {

            obj = cls.getDeclaredConstructor().newInstance();
            XSTREAM_TO_BEAN.fromXML(xmlStr, obj);
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            obj = (T) XSTREAM_TO_BEAN.fromXML(xmlStr);

        }
        return obj;
    }
           

問題 2 : 類屬性中使用了@XStreamAlias相同的類

如果同時具備兩個相同别名時,在序列化/反序列化時無法識别到對應的class,請在屬性上注解@XStreamAlias加上impl屬性,指定實作類,例:@XStreamAlias(impl = Instance.class, value = “instance”)

// 第一個類
@Data
@XStreamAlias(value = "instances")
public class Instance1 { ... }

// 第二個類
@Data
@XStreamAlias(value = "instances")
public class Instance2 { ... }


// 第三個類

@Data
@XStreamAlias(value = "instances")
public class A1 { 
	private Integer type;
	@XStreamAlias(impl = Instance.class, value = "instance")
    @XStreamImplicit(itemFieldName = "instance")
    private List<Instance1> instances;

}