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<ElementC> 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对象。结构图如下:


结构图

对于此种转化工具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);





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

<Elements>

        <Element key="key1">value1</ExtendInfo>

</Elements>

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

<Elements>

        <Element key="key1">

                <value1_key>value1</value1_key>

        </ExtendInfo>

</Elements>

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

所以说下第二种方法,XStream

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

使用XStream,需要添加依赖

    <groupId>com.thoughtworks.xstream

    <artifactId>xstream

    <version>1.4.10

</dependency>

如果飙红,试试改下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<ElementC> c_s;

@XStreamAlias("ElementD")

private String d;

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

private List<ElementE> ElementES;

}

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

@Data

@XStreamAlias("ElementE")

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

public class ElementE {

@XStreamAsAttribute()

private String key;

private String text;

}

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


结构图

此方法转换工具:

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 <T> 泛型

    * @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数据前后需要添加的一些声明,比如:

<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv="http://xxxxxxxxxx" xmlns:web="http://webservice.xxxxxxx/">

<soapenv:Body>

<web:insureService>

<key>value</key>

<datas><![CDATA

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

public StringhandlePostBody(XMLModel model){

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

    StringBuffer sb =new StringBuffer("");

    sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");

    sb.append("

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

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

    sb.append("<soapenv:Body>\n");

    sb.append("<web:insureService>\n");

    sb.append("<key>value</value>\n");

    sb.append("<datas><![CDATA[\n");

    sb.append(postBody);

    sb.append("]]></datas>\n");

    sb.append("</soapenv:Body>\n");

    sb.append("</soapenv:Envelope>");

    return sb.toString();

}



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

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

<RootA>

    <ElementB>

        <PropertyB1>xxx</PropertyB1>

        <PropertyB2>xxx</PropertyB2>

    </ElementB>

    <ElementAvaible>

        <PropertyB1>xxx</PropertyB1>

        <PropertyB2>xxx</PropertyB2>

        <ElementCS>

        <ElementC>

            <c1>xx</c1>

            <c2>xx</c2>

            ...

        </ElementC>

        </ElementCS>

        <ElementD>

            <d>xxx</d>

        </ElementD>

        <ElementES>

            <ElementE key="key">

                text

            </ElementE>

        </ElementES>

    </ElementAvaible>

</RootA>

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"),但是对于有外层包装的数据,比如

<root>

    <desc>this is only a example</desc>

    <namelist>

       <name>user01</name>

        <name>user02</name>

        <name>user03</name>

    </namelist>

    <infolist>

       <info>info01</info>

        <info>info02</info>

        <info>info03</info>

    </infolist>

</root>

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

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 处理节点中的属性和值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342