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("&","&");
@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("&", "&");
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;
}