对接项目和微信公众平台建设中,使用xml格式的报文比较多,一般java项目中会使用dom4j,jdom,SAX等解析方式,这里介绍用XStream解析xml的方法,支持属性和list等结构,具有清晰、简介的特点,具体方案如下:
1、定义与xml节点对应的bo类,假设xml文件如下:
<PackageList version="1.0">
<Package>
<Header>
<RequestType></RequestType>
<UUID></UUID>
<ComId></ComId>
<From></From>
<SendTime></SendTime>
<Asyn></Asyn>
<ReturnUrl></ReturnUrl>
<ProductCode></ProductCode>
</Header>
<Request>
<Order>
<TBOrderId></TBOrderId>
<TotalPremium></TotalPremium>
<PostFee></PostFee>
<ApplyNum></ApplyNum>
<Item>
<ItemId></ItemId>
<SkuRiskCode></SkuRiskCode>
<ProductCode></ProductCode>
<ProductName></ProductName>
<Premium></Premium>
<ActualPremium></ActualPremium>
<DiscountRate></DiscountRate>
</Item>
</Order>
<ApplyInfo>
<Holder>
<CustomList>
<Custom key="HolderName"></Custom>
<Custom key="save-holder"></Custom>
<Custom key="HolderCardType"></Custom>
<Custom key="HolderCardNo"></Custom>
<Custom key="HolderBirthday"></Custom>
<Custom key="HolderSex"></Custom>
<Custom key="HolderEmail"></Custom>
</CustomList>
</Holder>
<InsuredInfo>
<IsHolder></IsHolder>
<InsuredList>
<Insured>
<CustomList>
<Custom key="InsuredRelation"></Custom>
<Custom key="InsuredName"></Custom>
<Custom key="InsuredCardType"></Custom>
<Custom key="InsuredCardNo"></Custom>
<Custom key="InsuredBirthday"></Custom>
<Custom key="InsuredSex"></Custom>
<Custom key="InsuredEmail"></Custom>
</CustomList>
<BenefitInfo>
<IsLegal></IsLegal>
<BenefitList>
<Benefit>
<CustomList>
<Custom key="BenefitName"></Custom>
<Custom key="BenefitScale"></Custom>
<Custom key="BenefitRelation"></Custom>
</CustomList>
</Benefit>
</BenefitList>
</BenefitInfo>
</Insured>
</InsuredList>
</InsuredInfo>
<OtherInfo>
<CustomList/>
</OtherInfo>
<RefundInfo>
<CustomList/>
</RefundInfo>
</ApplyInfo>
</Request>
</Package>
</PackageList>
则我们可以定义对应的PackageList类:
@XStreamAlias("PackageList")
public class HBPackageList
{
private Attr version;
@XStreamAlias("Package")
private HBPackageInfo hbPackageInfo;
public HBPackageInfo getHbPackageInfo()
{
return hbPackageInfo;
}
public void setHbPackageInfo(HBPackageInfo hbPackageInfo)
{
this.hbPackageInfo = hbPackageInfo;
}
public Attr getVersion()
{
return version;
}
public void setVersion(Attr version)
{
this.version = version;
}
}这里的Attr为支持属性的bo类,内容如下:
public class Attr
{
private String attrValue;
public Attr(String attrValue)
{
this.attrValue = attrValue;
}
public String getAttrValue()
{
return attrValue;
}
public void setAttrValue(String attrValue)
{
this.attrValue = attrValue;
}
}@XstreamAlias用注解来重命名属性的名称,HBPackageInfo对应PackageList的一级子节点,如下:
@XStreamAlias("Package")
public class HBPackageInfo
{
@XStreamAlias("Header")
private Header header;
@XStreamAlias("Request")
private HBRequest hbRequest;
public Header getHeader()
{
return header;
}
public void setHeader(Header header)
{
this.header = header;
}
public HBRequest getHbRequest()
{
return hbRequest;
}
public void setHbRequest(HBRequest hbRequest)
{
this.hbRequest = hbRequest;
}
}
如此这样循环嵌套定义,即可定义出一套完整的xml结构对应的类结构......
我们注意到,某些子节点如CustomList,下面是一个列表对应的子节点,并且每个子节点都有一个不同的key值,这种情况下我们可以用Map结构来描述它,例如ApplyInfo的定义如下:
@XStreamAlias("ApplyInfo")
public class HBApplyInfo
{
@XStreamAlias("Holder")
private CustomList holder;
@XStreamAlias("InsuredInfo")
private HBInsuredInfo hbInsuredInfo;
@XStreamAlias("OtherInfo")
private CustomList otherInfo;
@XStreamAlias("RefundInfo")
private CustomList refundInfo;
public HBInsuredInfo getHbInsuredInfo()
{
return hbInsuredInfo;
}
public void setHbInsuredInfo(HBInsuredInfo hbInsuredInfo)
{
this.hbInsuredInfo = hbInsuredInfo;
}
public CustomList getHolder()
{
return holder;
}
public void setHolder(CustomList holder)
{
this.holder = holder;
}
public CustomList getOtherInfo()
{
return otherInfo;
}
public void setOtherInfo(CustomList otherInfo)
{
this.otherInfo = otherInfo;
}
public CustomList getRefundInfo()
{
return refundInfo;
}
public void setRefundInfo(CustomList refundInfo)
{
this.refundInfo = refundInfo;
}
}其中CustomList的定义如下:
public class CustomList
{
@XStreamAlias("CustomList")
private Map<String,String> customMap = new HashMap<String,String>();
public Map<String, String> getCustomMap()
{
return customMap;
}
public void setCustomMap(Map<String, String> customMap)
{
this.customMap = customMap;
}
}还有一种情况,某些节点使用CDATA的结构包装节点间的数据时,需要对CDATA节点进行解析,这时我们自定义注解类如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface XStreamCDATA
{
}
使用时如下:
public class ResBaseMessage
{
@XStreamCDATA
private String ToUserName;
@XStreamCDATA
private String FromUserName;
private String CreateTime;
@XStreamCDATA
private String MsgType;
public String getToUserName()
{
return ToUserName;
}
public void setToUserName(String toUserName)
{
ToUserName = toUserName;
}
public String getFromUserName()
{
return FromUserName;
}
public void setFromUserName(String fromUserName)
{
FromUserName = fromUserName;
}
public String getCreateTime()
{
return CreateTime;
}
public void setCreateTime(String createTime)
{
CreateTime = createTime;
}
public String getMsgType()
{
return MsgType;
}
public void setMsgType(String msgType)
{
MsgType = msgType;
}
}
2、定义驱动器
请注意上述Attr、Map和XStreamCDATA不是天然就支持的,需要自定义驱动器来支持它,Attr的驱动器定义如下:
public class AttrConverter implements SingleValueConverter
{
public boolean canConvert(Class clazz)
{
return clazz.equals(Attr.class);
}
public String toString(Object obj)
{
return ((Attr)obj).getAttrValue();
}
public Object fromString(String str)
{
return new Attr(str);
}
}Map的驱动器定义如下:
/**
*
* @类名: MapCustomConverterUtils.java
* @描述:XML解析转化工具,主要是支持将Map转化为特定的格式,如:<node key="xxx">xxx</node>
* @作者: mxyanx
* @修改日期: 2014年7月1日
*/
public class MapCustomConverterUtils extends AbstractCollectionConverter {
public MapCustomConverterUtils(Mapper mapper) {
super(mapper);
}
/**
* 是否支持的转换Map类型
*/
public boolean canConvert(Class type) {
return type.equals(HashMap.class)
|| type.equals(Hashtable.class)
|| type.getName().equals("java.util.LinkedHashMap")
|| type.getName().equals("sun.font.AttributeMap");
}
/**
* 生成xml文件时的处理方法,将key值set到属性中,将value值set到节点中
*/
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
Map map = (Map) source;
for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
Entry entry = (Entry) iterator.next();
ExtendedHierarchicalStreamWriterHelper.startNode(writer, "Custom", Entry.class);
writer.addAttribute("key", entry.getKey().toString());
writer.setValue(entry.getValue().toString());
writer.endNode();
}
}
/**
* 解析xml文件时的处理方法
*/
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Map map = (Map) createCollection(context.getRequiredType());
populateMap(reader, context, map);
return map;
}
/**
*
* 功能描述:由xml文件的节点计算Map的key和value,返回map结构
* @param reader
* @param context
* @param map
*/
protected void populateMap(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) {
while (reader.hasMoreChildren()) {
reader.moveDown();
Object key = reader.getAttribute("key");
Object value = reader.getValue();
map.put(key, value);
reader.moveUp();
}
}
}CDATA的驱动器如下:
public class CDATAXppDriver extends XppDriver
{
public HierarchicalStreamWriter createWriter(Writer out)
{
return new CDATAPrettyPrintWriter(out);
}
public static XStream createXstream()
{
return new XStream(new XppDriver() {
@Override
public HierarchicalStreamWriter createWriter(Writer out)
{
return new PrettyPrintWriter(out) {
boolean cdata = false;
Class<?> targetClass = null;
@Override
public void startNode(String name, @SuppressWarnings("rawtypes")
Class clazz)
{
super.startNode(name, clazz);
// 业务处理,对于用XStreamCDATA标记的Field,需要加上CDATA标签
if (!name.equals("xml") && !name.equals("Articles") && !name.equals("item"))
{
cdata = needCDATA(targetClass, name);
}
else
{
targetClass = clazz;
}
}
@Override
protected void writeText(QuickWriter writer, String text)
{
if (cdata)
{
writer.write("<![CDATA[" + text + "]]>");
}
else
{
writer.write(text);
}
}
};
}
});
}
private static boolean needCDATA(Class<?> targetClass, String fieldAlias)
{
boolean cdata = false;
cdata = existsCDATA(targetClass, fieldAlias);
if (cdata)
{
return cdata;
}
Class<?> superClass = targetClass.getSuperclass();
while (!superClass.equals(Object.class))
{
cdata = existsCDATA(superClass, fieldAlias);
if (cdata)
{
return cdata;
}
superClass = superClass.getClass().getSuperclass();
}
return false;
}
/**
*
* 功能描述:判断节点是否有CDATA的注解
* @param clazz
* @param fieldAlias
* @return
*/
private static boolean existsCDATA(Class<?> clazz, String fieldAlias)
{
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields)
{
if (field.getAnnotation(XStreamCDATA.class) != null)
{
XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
if (null != xStreamAlias)
{
if (fieldAlias.equals(xStreamAlias.value()))
{
return true;
}
}
else
{
if (fieldAlias.equals(field.getName()))
{
return true;
}
}
}
}
return false;
}
}public class CDATAPrettyPrintWriter implements ExtendedHierarchicalStreamWriter
{
private final QuickWriter writer;
private final FastStack elementStack = new FastStack(16);
private final char[] lineIndenter;
private boolean tagInProgress;
private int depth;
private boolean readyForNewLine;
private boolean tagIsEmpty;
private static final char[] AMP = "&".toCharArray();
private static final char[] LT = "<".toCharArray();
private static final char[] GT = ">".toCharArray();
private static final char[] SLASH_R = " ".toCharArray();
private static final char[] QUOT = """.toCharArray();
private static final char[] APOS = "'".toCharArray();
private static final char[] CLOSE = "</".toCharArray();
public CDATAPrettyPrintWriter(Writer writer, char[] lineIndenter)
{
this.writer = new QuickWriter(writer);
this.lineIndenter = lineIndenter;
}
public CDATAPrettyPrintWriter(Writer writer, String lineIndenter)
{
this(writer, lineIndenter.toCharArray());
}
public CDATAPrettyPrintWriter(PrintWriter writer)
{
this(writer, new char[] { ' ', ' ' });
}
public CDATAPrettyPrintWriter(Writer writer)
{
this(new PrintWriter(writer));
}
public void startNode(String name)
{
tagIsEmpty = false;
finishTag();
writer.write('<');
writer.write(name);
elementStack.push(name);
tagInProgress = true;
depth++;
readyForNewLine = true;
tagIsEmpty = true;
}
public void startNode(String name, Class clazz)
{
startNode(name);
}
public void setValue(String text)
{
readyForNewLine = false;
tagIsEmpty = false;
finishTag();
writeText(writer, text);
}
public void addAttribute(String key, String value)
{
writer.write(' ');
writer.write(key);
writer.write('=');
writer.write('"');
writeAttributue(writer, value);
writer.write('"');
}
protected void writeAttributue(QuickWriter writer, String text)
{
int length = text.length();
for (int i = 0; i < length; i++)
{
char c = text.charAt(i);
switch (c)
{
case '&':
this.writer.write(AMP);
break;
case '<':
this.writer.write(LT);
break;
case '>':
this.writer.write(GT);
break;
case '"':
this.writer.write(QUOT);
break;
case '\'':
this.writer.write(APOS);
break;
case '\r':
this.writer.write(SLASH_R);
break;
default:
this.writer.write(c);
}
}
}
protected void writeText(QuickWriter writer, String text)
{
int length = text.length();
String CDATAPrefix = "<
1561

被折叠的 条评论
为什么被折叠?



