Xstream處理XML生成中null值的復雜情況


前一段時間和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由於需求原因增加了數字類型的判斷,並在輸出時也做了判斷。

這是第一次在園子里發隨筆,寫的不好但是是想從現在開始記錄自己的學習生活。想養成寫博客的習慣。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM