前一段時間和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由於需求原因增加了數字類型的判斷,並在輸出時也做了判斷。
這是第一次在園子里發隨筆,寫的不好但是是想從現在開始記錄自己的學習生活。想養成寫博客的習慣。