Java对象和Xml数据的相互转换

最近项目有一个跟三方交互的接口,三方用的xml数据,简单记录下玩xml的历程。

首先,对于正常的xml解析,推荐JAXB(Java Architecture for XML Binding) ,JAXB是一个业界的标准,可以根据XML Schema产生Java类的技术。

JDK中JAXB相关的重要Annotation:

@XmlType,将Java类或枚举类型映射到XML模式类型

@XmlAccessorType(XmlAccessType.FIELD) ,控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标注)字段到XML。其他值还有XmlAccessType.PROPERTY和XmlAccessType.NONE。

@XmlAccessorOrder,控制JAXB 绑定类中属性和字段的排序。

@XmlJavaTypeAdapter,使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML。

@XmlElementWrapper ,对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。

@XmlRootElement,将Java类或枚举类型映射到XML元素。

@XmlElement,将Java类的一个属性映射到与属性同名的一个XML元素。

@XmlAttribute,将Java类的一个属性映射到与属性同名的一个XML属性。

在以上的注解中,用的最多的是@XMLType,@XmlAccessorType,@XmlRootElement。

举个例子:

import javax.xml.bind.annotation.*;

import java.util.ArrayList;

import java.util.List;

import lombok.Data;

@Data

@XmlAccessorType(XmlAccessType.FIELD)

@XmlRootElement(name = "RootA")

public class RootA {

@XmlElement(name = "ElementB")

private ElementB b;

@XmlElementWrapper(name="ElementCS")

@XmlElement(name="ElementC")

private List c_s;

@XmlElement(name = "ElementD")

private String d;

}

对于对象B,它的另外一个对象,假设结构如下:

@Data

@XmlAccessorType(XmlAccessType.FIELD)

public class B {

@XmlElement(name = "PropertyB1")

 private propertyB1;

@XmlElement(name = "PropertyB2")

 private String propertyB2;

}

如此,我们得到一个对象A,xml的数据大概为:

A为根节点,下面有B、C、D 三个对象组成,其中B为另一个对象,C为一个复杂对象List,由数个c构成(c可包含其他复杂对象,如List),D为一个简单的String对象。结构图如下:


Java对象和Xml数据的相互转换_第1张图片
结构图

对于此种转化工具XmlUtil:

import javax.xml.bind.JAXBContext;

import javax.xml.bind.JAXBException;

import javax.xml.bind.Marshaller;

import javax.xml.bind.Unmarshaller;

import java.io.*;


public class XmlUtil {

/**

* 将对象直接转换成String类型的XML输出

    * @param obj 指定对象(包含XML注解)

    * @return 返回XML

*/

    public static StringconvertToXml(Object obj) {

// 创建输出流

        StringWriter sw =new StringWriter();

        try {

// 利用jdk中自带的转换类实现

            JAXBContext context = JAXBContext.newInstance(obj.getClass());

            // 将对象序列化为Xml

            Marshaller marshaller = context.createMarshaller();

            // 格式化Xml输出的格式

            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            // 将对象转换成输出流形式的XML

            marshaller.marshal(obj, sw);

        }catch (JAXBException e) {

e.printStackTrace();

        }

return sw.toString();

    }

/**

* 将file类型的xml装换成对象

*/

    @SuppressWarnings("unchecked")

public static T convertXmlFileToT(Class clazz,String xmlPath) {

T xmlObject =null;

        FileReader fr =null;

        try {

JAXBContext context = JAXBContext.newInstance(clazz);

            Unmarshaller unmarshal =  context.createUnmarshaller();

            fr =new FileReader(xmlPath);

            xmlObject = (T) unmarshal.unmarshal(fr);

        }catch (JAXBException e) {

e.printStackTrace();

        }catch (FileNotFoundException e) {

e.printStackTrace();

        }finally {

try {

if(fr !=null) {

fr.close();

                }

}catch (IOException e) {

// TODO Auto-generated catch block

                e.printStackTrace();

            }

}

return xmlObject;

    }

/**

* 将String类型的Xml转换成对象

*/

    @SuppressWarnings("unchecked")

public static T convertXmlStrToT(Class clazz, String xmlStr) {

T xmlObject =null;

        try {

JAXBContext context = JAXBContext.newInstance(clazz);

            // 进行将Xml转成对象的核心接口

            Unmarshaller unmarshaller = context.createUnmarshaller();

            StringReader sr =new StringReader(xmlStr);

            xmlObject = (T) unmarshaller.unmarshal(sr);

        }catch (JAXBException e) {

e.printStackTrace();

        }

return xmlObject;

    }

/**

* 根据xml路径将其转为对象

*/

    public StringconvertPathToT(Class clazz, String path) {

JAXBContext context =null;

        // 创建输出流

        FileWriter fw =null;

        try {

context = JAXBContext.newInstance(clazz);

            Marshaller marshaller = context.createMarshaller();

            // 格式化xml输出的格式

            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            // 将对象转换成输出流形式的xml

            try {

fw =new FileWriter(path);

            }catch (Exception e) {

e.printStackTrace();

            }

marshaller.marshal(clazz, fw);

        }catch (JAXBException e1) {

// TODO Auto-generated catch block

            e1.printStackTrace();

        }

return fw.toString();

    }

}

对于上述方法,最重要的就是梳理清楚xml的结构,创建对应的类和属性,转换过程很简单,xml作为String输入。

RootA rootA = XmlUtil.convertXmlStrToT(RootA.class,xmlStr);

XmlStr = = XmlUtil.convertToXml(RootA);





在此之后遇到了一个问题,对于

        value1

对比下一般情况下的结构:

        

                value1

        

这种类型的。Elements为Element的List集合。单一对象,key可以用注解@XmlAttribute(name = "key")来解决,但value1无法在不设置key的形式下去set。

所以说下第二种方法,XStream

XStream优先很多,不在意java类中成员变量是私有还是公有,也不在乎是否有默认构造函数。它调用方式也非常简单:从xml对象转化为java对象,使用fromXML()方法;从java对象序列化为xml,toXML()即可。

使用XStream,需要添加依赖

    com.thoughtworks.xstream

    xstream

    1.4.10

如果飙红,试试改下version。

另外,对于在定义别名中的下划线“_”转换为xml后会变成“__”这个符号, 不过下划线问题可以用下面这个解决

1.str.replaceAll(“__“,“_“);

2.XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("__", "_")));

3.//使用xstream自带的NoNameCoder构造xstream,该方式将导致所有特殊字符都不转义XStream xstream = new XStream(new XppDriver(new NoNameCoder()));

//使用Domdriver及NonameCoder构造xstream,该方式可以指定xml编码方式XStream xstream = new XStream(new DomDriver("UTF-8", new NoNameCoder()));

还是举例说明:

import com.thoughtworks.xstream.annotations.XStreamAlias;

import com.thoughtworks.xstream.annotations.XStreamConverter;

import com.thoughtworks.xstream.annotations.XStreamImplicit;

import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter;

import lombok.Data;

import java.util.ArrayList;

import java.util.List;


@Data

@XStreamAlias("RootA")

public class RootA {

@XStreamAlias("ElementB")

private ElementB b;

@XStreamAlias(value ="ElementCS", impl = List.class)

private List c_s;

@XStreamAlias("ElementD")

private String d;

@XStreamAlias(value = "ElementES", impl = List.class) 

private List ElementES;

}

对于B、C、D我们保持上个例子,对于E,我们定义如下:

@Data

@XStreamAlias("ElementE")

@XStreamConverter(value = ToAttributedValueConverter.class,strings ="text")

public class ElementE {

@XStreamAsAttribute()

private String key;

private String text;

}

如此,我们就可以得到目标结构,如下:


Java对象和Xml数据的相互转换_第2张图片
结构图

此方法转换工具:

import com.thoughtworks.xstream.XStream;

import com.thoughtworks.xstream.io.xml.DomDriver;

import org.apache.commons.lang3.StringUtils;

import org.jdom2.Document;

import org.jdom2.JDOMException;

import com.alibaba.fastjson.JSONObject;

import org.jdom2.Element;

import org.jdom2.input.SAXBuilder;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.LinkedList;

import java.util.List;


public class XMLToJson {

/**

* 将Object转换为xml

    * @param obj 转换的bean

    * @return bean转换为xml

*/

    public static StringobjectToXml(Object obj) {

XStream xStream =new XStream();

        //xstream使用注解转换

        xStream.processAnnotations(obj.getClass());

        return xStream.toXML(obj);

    }

/**

* 将xml转换为T

    * @param 泛型

    * @param xml 要转换为T的xml

    * @param cls T对应的Class

    * @return xml转换为T

*/

    public static T xmlToObject(String xml, Class cls){

XStream xstream =new XStream(new DomDriver());

        //xstream使用注解转换

        xstream.processAnnotations(cls);

        return (T) xstream.fromXML(xml);

    }

}





补充:对于xml数据前后需要添加的一些声明,比如:

value

可以使用如下方法进行包装:

public StringhandlePostBody(XMLModel model){

String postBody = XMLToJson.objectToXml(model);//XML对应的实体类转化为String类型的xml文本

    StringBuffer sb =new StringBuffer("");

    sb.append("\n");

    sb.append("

            +"xmlns:soapenv=\"xxxxxx\""

            +"xmlns:web=\"http://webservice.xxxxxx\">\n");

    sb.append("\n");

    sb.append("\n");

    sb.append("value\n");

    sb.append("

    sb.append(postBody);

    sb.append("]]>\n");

    sb.append("\n");

    sb.append("");

    return sb.toString();

}



当我们获取到一长串的xml文本数据,但是有些并不是我们需要的,比如大串的声明、无关紧要的节点数据等。我们需要截取其中的一部分来为我们所用

例如如下数据,我们只需要 ElementAvaible 这个节点内的数据赋值给xmlString,

   

        xxx

        xxx

   

   

        xxx

        xxx

       

       

            xx

            xx

            ...

       

       

       

            xxx

       

       

           

                text

           

       

   

SAXReader reader =new SAXReader();

Document document =null;

try {

document = (Document) DocumentHelper.parseText(result);

    Node EspaNode = document.selectSingleNode(".//*[local-name()='ElementAvaible']");//返回xml中ElementAvaible节点的数据

    String note = EspaNode.getText();//获取节点数据

    xmlString = note;

}catch (DocumentException e) {

e.printStackTrace();

}catch (Exception e) {

e.printStackTrace();

}



问题解决了,简单总结下。。。

对于JAXB:

1.一种是转换成对象和string类型的xml转换,一种是对象和xml文件进行转换。Java对象和XML文件相互操作有两种方式,

import java.io.FileNotFoundException; 

import java.io.FileReader; 

import java.io.FileWriter; 

import java.io.IOException; 

import java.io.StringReader; 

import java.io.StringWriter; 


import javax.xml.bind.JAXBContext; 

import javax.xml.bind.JAXBException; 

import javax.xml.bind.Marshaller; 

import javax.xml.bind.Unmarshaller; 


/**

* 封装了XML转换成object,object转换成XML的代码

* @author Steven

*/ 

public class XMLUtil { 

    /**

    * 将对象直接转换成String类型的 XML输出

    * 

    * @param obj

    * @return

    */ 

    public static String convertToXml(Object obj) { 

        // 创建输出流 

        StringWriter sw = new StringWriter(); 

        try { 

            // 利用jdk中自带的转换类实现 

            JAXBContext context = JAXBContext.newInstance(obj.getClass()); 


            Marshaller marshaller = context.createMarshaller(); 

            // 格式化xml输出的格式 

            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 

                    Boolean.TRUE); 

            // 将对象转换成输出流形式的xml 

            marshaller.marshal(obj, sw); 

        } catch (JAXBException e) { 

            e.printStackTrace(); 

        } 

        return sw.toString(); 

    } 


    /**

    * 将对象根据路径转换成xml文件

    * 

    * @param obj

    * @param path

    * @return

    */ 

    public static void convertToXml(Object obj, String path) { 

        try { 

            // 利用jdk中自带的转换类实现 

            JAXBContext context = JAXBContext.newInstance(obj.getClass()); 


            Marshaller marshaller = context.createMarshaller(); 

            // 格式化xml输出的格式 

            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 

                    Boolean.TRUE); 

            // 将对象转换成输出流形式的xml 

            // 创建输出流 

            FileWriter fw = null; 

            try { 

                fw = new FileWriter(path); 

            } catch (IOException e) { 

                e.printStackTrace(); 

            } 

            marshaller.marshal(obj, fw); 

        } catch (JAXBException e) { 

            e.printStackTrace(); 

        } 

    } 


    @SuppressWarnings("unchecked") 

    /**

    * 将String类型的xml转换成对象

    */ 

    public static Object convertXmlStrToObject(Class clazz, String xmlStr) { 

        Object xmlObject = null; 

        try { 

            JAXBContext context = JAXBContext.newInstance(clazz); 

            // 进行将Xml转成对象的核心接口 

            Unmarshaller unmarshaller = context.createUnmarshaller(); 

            StringReader sr = new StringReader(xmlStr); 

            xmlObject = unmarshaller.unmarshal(sr); 

        } catch (JAXBException e) { 

            e.printStackTrace(); 

        } 

        return xmlObject; 

    } 

    @SuppressWarnings("unchecked") 

    /**

    * 将file类型的xml转换成对象

    */ 

    public static Object convertXmlFileToObject(Class clazz, String xmlPath) { 

        Object xmlObject = null; 

        try { 

            JAXBContext context = JAXBContext.newInstance(clazz); 

            Unmarshaller unmarshaller = context.createUnmarshaller(); 

            FileReader fr = null; 

            try { 

                fr = new FileReader(xmlPath); 

            } catch (FileNotFoundException e) { 

                e.printStackTrace(); 

            } 

            xmlObject = unmarshaller.unmarshal(fr); 

        } catch (JAXBException e) { 

            e.printStackTrace(); 

        } 

        return xmlObject; 

    } 

}

2.需要转换的model对象一定要添加@XmlRootElement注解,其里面的其他对象则不需要;

3.需要转换的model对象一定要有不带参数的构造方法,包括该对象里面引用的对象



对于XStream:

1.@XStreamAlias(value = "listName", impl = List.class) 只适用复杂对象类型的转换,简单类型的可以使用@XStreamImplicit(itemFieldName ="listName"),但是对于有外层包装的数据,比如

    this is only a example

   

       user01

        user02

        user03

   

   

       info01

        info02

        info03

   

则需要分别处理对应节点的数据,

XStream xstream = new XStream(new DomDriver());

xstream.processAnnotations(Rootinfo.class);

ClassAliasingMapper mapper = new ClassAliasingMapper(xstream.getMapper());

mapper.addClassAlias("name", String.class);

xstream.registerLocalConverter(

    Rootinfo.class, "namelist",

    new CollectionConverter(mapper)

);

Rootinfo strXML=(Rootinfo)xstream.fromXML(strXML);

2.XStreamAsAttribute 作用是将类内成员作为父节点属性输出,等同于XStream.useAttributeFor(Object.class, "attribute"),如例子中的ElementE;

XStreamAlias("object") 等同于 xstream.alias("object", Object.class); 

XStreamConverter xstreamConvert用于指定class及Field的converter(转换方式),如例子中的ElementE。 

XStreamImplicit 注解使用当需要将collection或map类型的成员变量中数据转换成xml相同层次的元素时,可以在该成员变量使用该注解,会将添加注释的节点去掉 @XStreamImplicit(itemFieldName="xxx")

@XStreamOmitField 表明该属性不会被序列化到xml中

3.对于xml转javabean,xstream默认的所有converter均不支持泛型、接口。如果存在超类时,xml中存在子类属性时,转换将出现异常,不包含子类属性时,可转换成功

参考:使用XStream注解处理复杂xml的属性及数据集合(xml转对象)

            Java -- XStreamAlias 处理节点中的属性和值

你可能感兴趣的:(Java对象和Xml数据的相互转换)