使用XStream解析和生成xml文件

         对接项目和微信公众平台建设中,使用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 = "<![CDATA[";
        if (!text.startsWith(CDATAPrefix))
        {
            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);
                }
            }
        }
        else
        {
            for (int i = 0; i < length; i++)
            {
                char c = text.charAt(i);
                this.writer.write(c);
            }
        }
    }


    public void endNode()
    {
        depth--;
        if (tagIsEmpty)
        {
            writer.write('/');
            readyForNewLine = false;
            finishTag();
            elementStack.popSilently();
        }
        else
        {
            finishTag();
            writer.write(CLOSE);
            writer.write((String) elementStack.pop());
            writer.write('>');
        }
        readyForNewLine = true;
        if (depth == 0)
        {
            writer.flush();
        }
    }


    private void finishTag()
    {
        if (tagInProgress)
        {
            writer.write('>');
        }
        tagInProgress = false;
        if (readyForNewLine)
        {
            endOfLine();
        }
        readyForNewLine = false;
        tagIsEmpty = false;
    }


    protected void endOfLine()
    {
        writer.write('\n');
        for (int i = 0; i < depth; i++)
        {
            writer.write(lineIndenter);
        }
    }


    public void flush()
    {
        writer.flush();
    }


    public void close()
    {
        writer.close();
    }


    public HierarchicalStreamWriter underlyingWriter()
    {
        return this;
    }

}

3、解析

       在解析属性时,需要用到useAttributeFor方法,在处理Map结构的字段和CDATA数据时,都需要用到驱动器,在处理重命名节点时,需要用到processAnnotations方法,例如:

/**
     * 
     * 功能描述:解析xml文件,并将CustomList的节点以key:value格式输出
     * @throws FileNotFoundException
     */
    @Test
    public void testHbXml2BeanParser() throws FileNotFoundException{
        XStream hbXStream = new XStream(new DomDriver());  
        HBPackageList packageList = new HBPackageList();
        FileInputStream hbFis = new FileInputStream("d:/file/hb.xml");  
        hbXStream.useAttributeFor(HBPackageList.class, "version");  
        hbXStream.registerConverter(new AttrConverter());  
        hbXStream.processAnnotations(HBPackageList.class);
        hbXStream.registerConverter(new MapCustomConverterUtils(new DefaultMapper(HBPackageList.class.getClassLoader()))); 
        hbXStream.fromXML(hbFis, packageList); 
        //打印出Holder节点的CustomList包含的Custom节点
        Map<String,String> holderCustomMap = packageList.getHbPackageInfo().getHbRequest().getHbApplyInfo().getHolder().getCustomMap();
        System.out.println("************orderid:"+packageList.getHbPackageInfo().getHbRequest().getOrder().getTbOrderId());
        for(String key : holderCustomMap.keySet()){
            System.out.println(key +":"+holderCustomMap.get(key));
        }
        System.out.println("*********************");
     
        List<HBInsured> hbInsuredList = packageList.getHbPackageInfo().getHbRequest().getHbApplyInfo().getHbInsuredInfo().getInsuredList();
        for(HBInsured h : hbInsuredList){
            HBBenefitInfo benefitInfo = h.getHbBenefitInfo();
            List<HBBenefit> hbBenefitList =  benefitInfo.getHbBenefitList();
            for(HBBenefit b : hbBenefitList){
                Map<String,String> customMap = b.getCustomMap();
                for(String key : customMap.keySet()){
                    System.out.println("key:"+key+",value:"+customMap.get(key));
                }
            }
        }
        System.out.println("Version:"+packageList.getVersion().getAttrValue());
        
    }

生成xml文件:

  /**
     * 
     * 功能描述:生成xml文件
     * @throws FileNotFoundException
     */
    @Test
    public void testHbResponseBean2Xml() throws FileNotFoundException{
        HBResponsePackageList packageList = new HBResponsePackageList();
        XStream xStream = new XStream();  
        packageList.getHbResponsePackageInfo().getResponseHeader().setComId("123");
        packageList.getHbResponsePackageInfo().getHbResponseResponse().getHbResponseProposal().setFailReason("成功");
        xStream.processAnnotations(HBResponsePackageList.class);
        FileOutputStream fs = new FileOutputStream("d:/file/hbResponse.xml");
        xStream.toXML(packageList, fs);
    }


解析CDATA的数据节点:

@Test
    public void testParserTextMessageXml() throws FileNotFoundException
    {
        XStream txtMsgXStream = new XStream(new CDATAXppDriver());
        TextMessage textMessage = new TextMessage();
        FileInputStream txtMsgFis = new FileInputStream("d:/file/textMessage.xml");
        txtMsgXStream.processAnnotations(TextMessage.class);
        txtMsgXStream.fromXML(txtMsgFis, textMessage);
        System.out.println("FromUserName:" + textMessage.getFromUserName());
        System.out.println("content:" + textMessage.getContent());

    }


生成CDATA的数据节点:

@Test
    public void testCreateCDATAXml() throws FileNotFoundException
    {
        ResTextMessage resTextMessage = new ResTextMessage();
        resTextMessage.setToUserName("color");
        resTextMessage.setFromUserName("yan");
        resTextMessage.setCreateTime("" + System.currentTimeMillis());
        resTextMessage.setMsgType("text");
        resTextMessage.setContent("你好!");
        XStream xStream = CDATAXppDriver.createXstream();
        xStream.processAnnotations(ResTextMessage.class);
        FileOutputStream fs = new FileOutputStream("d:/file/ResTextMessage.xml");
        xStream.toXML(resTextMessage, fs);
    }


上述代码都经过测试,可以直接使用。































  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值