前一段時間和Xstream打過交道,發現Xstream在支援json以及XML方面還是相當強大的。提供annotation註解,可以在JavaBean中完成對xml節點、屬性的描述。在根據xsd轉換為Xstream模型之後,希望利用Xstream來產生XML並且滿足XSD要求。例如:
package nju.software.ExecutionInterfaces.service.XstreamModels;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlElement;import javax.xml.bind.annotation.XmlRootElement;import javax.xml.bind.annotation.XmlType;import com.thoughtworks.xstream.annotations.XStreamAlias;@XmlAccessorType(XmlAccessType.FIELD)@XmlType(name = "", propOrder = { "userId", "infoCode", "departId"})@XmlRootElement(name = "info")@XStreamAlias("info")@Annotation(documentation="資訊")public class Info { @XmlElement(name = "user_id")@XStreamAlias("user_id")@Annotation(documentation="使用者ID") protected int userId; @XmlElement(name = "info_code", required = true)@XStreamAlias("info_code")@Annotation(documentation="資訊密碼") protected String infoCode; @XmlElement(name = "depart_id", required = true, type = Integer.class, nillable = true)@XStreamAlias("depart_id")@Annotation(documentation="部門ID") protected Integer departId;}
根據XSD產生的模型加上Xstream註解。利用xStream中toXML()方法將Model轉換成XML字串。預想是僅僅使用processAnnotations方式注入註解就自動將XML中節點名稱寫好。但是產生的XML卻無法通過XSD的驗證。經過檢查發現是xsd中類型設定的原因。比如departId欄位。required = true, type = Integer.class, nillable = true,表示為Integer類型且可為空白。但是當真正將值設為空白時又通不過驗證。因為XSD在檢查數字類型的時候需要至少寫一個0。<depart_id/>或者<depart_id></depart_id>都不行。於是想到了使用Xstream中的Converter介面。在園子裡也看到了有關nullConverter的說明。在其基礎上進行了修改。
import java.lang.reflect.Field;import java.lang.reflect.Method;import java.math.BigDecimal;import java.text.DecimalFormat;import java.util.List;import com.thoughtworks.xstream.annotations.XStreamAlias;import com.thoughtworks.xstream.converters.Converter;import com.thoughtworks.xstream.converters.MarshallingContext;import com.thoughtworks.xstream.converters.UnmarshallingContext;import com.thoughtworks.xstream.io.HierarchicalStreamReader;import com.thoughtworks.xstream.io.HierarchicalStreamWriter;public class NullConverter implements Converter {@SuppressWarnings("unchecked")public void marshal(Object source, HierarchicalStreamWriter writer,MarshallingContext context) {if (null == source)return;Class cType = source.getClass();Field[] fields = cType.getDeclaredFields();if (source instanceof List) {List list = (List) source;for (Object obj : list) {XStreamAlias alias = obj.getClass().getAnnotation(XStreamAlias.class);if (alias != null) {writer.startNode(alias.value());marshal(obj, writer, context);writer.endNode();}else {marshal(obj, writer, context);}}} else {for (Field field : fields) {//獲得get方法String temp1 = "get"+ field.getName().substring(0, 1).toUpperCase()+ field.getName().substring(1);Method m = null;try {m = cType.getMethod(temp1, null);} catch (SecurityException e1) {e1.printStackTrace();} catch (NoSuchMethodException e1) {e1.printStackTrace();}String methodName = m.getName();if (methodName.indexOf("get") != -1 && methodName != "getClass") {boolean isBaseType = isBaseType(m.getReturnType());String name = methodName.substring(3);Object o = null;try {o = m.invoke(source, null);} catch (Exception e) {e.printStackTrace();}//遞迴打出基礎類型值if (isBaseType) {writer.startNode(getAliasByNameAndType(name, cType).value());writeData(o, m.getReturnType(), writer);writer.endNode();} else {XStreamAlias alias = getAliasByNameAndType(name, cType);if (alias == null) {marshal(o, writer, context);} else {writer.startNode(alias.value());marshal(o, writer, context);writer.endNode();}}}}}}/** * 根據Name和類獲得Xstream註解 * @param name * Name * @param cType * 類 * @return * XStreamAlias */private XStreamAlias getAliasByNameAndType(String name,Class<?> cType){String temp = name.substring(0, 1).toLowerCase()+ name.substring(1);Field f = null;try {f = cType.getDeclaredField(temp);} catch (SecurityException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}XStreamAlias alias = f.getAnnotation(XStreamAlias.class);return alias;}/** * 改寫輸出XML * @param o * @param ReturnType * @param writer */private void writeData(Object o,Class<?> ReturnType,HierarchicalStreamWriter writer) {//如果是數字類型的話就要預設為0而不可為空if (isNumValueType(ReturnType)) {if (o == null) {writer.setValue("0");}else if (ReturnType.equals(Double.class)||ReturnType.equals(double.class)||ReturnType.equals(BigDecimal.class)) {DecimalFormat df = new DecimalFormat("#.##");writer.setValue(df.format(o));}else {writer.setValue(o.toString());}} else {writer.setValue(o == null ? "" : o.toString());}}public Object unmarshal(HierarchicalStreamReader reader,UnmarshallingContext context) {return null;}public boolean canConvert(Class type) {return true;}/** * 判斷是否為基本類型 * @param type * @return * boolean */private boolean isBaseType(Class<?> type) {if (type.equals(Integer.class) || type.equals(Double.class)|| type.equals(String.class) || type.equals(Boolean.class)|| type.equals(Long.class) || type.equals(Short.class)|| type.equals(Byte.class) || type.equals(Float.class)|| type.equals(BigDecimal.class) || type.equals(int.class)|| type.equals(float.class) || type.equals(long.class)|| type.equals(double.class) || type.equals(short.class)|| type.equals(boolean.class) || type.equals(byte.class)) {return true;}return false;}/** * 判斷是否為數字類型 * @param type * @return * boolean */public boolean isNumValueType(Class<?> type) {if (type.equals(Integer.class) || type.equals(Double.class)|| type.equals(Long.class) || type.equals(Short.class)|| type.equals(Float.class) || type.equals(BigDecimal.class)|| type.equals(int.class) || type.equals(float.class)|| type.equals(long.class) || type.equals(double.class)|| type.equals(short.class)) {return true;}return false;}}
基本思想還是利用遞迴的思想,主要修改有這麼幾點:
1打出一個類中屬性XML節點的方式。原始方式是通過拼接出get方法,便利獲得屬性上的Xstream註解產生。但是這樣產生不能保證屬性的順序,於是改成getAliasByNameAndType方法中首先獲得屬性。根據屬性拼接get方法。
2由於需求原因增加了數字類型的判斷,並在輸出時也做了判斷。
這是第一次在園子裡發隨筆,寫的不好但是是想從現在開始記錄自己的學習生活。想養成寫部落格的習慣。