JAXB - xml与对象的转换

在与第三方进行数据交互时,对方要求以 xml 格式进行数据传输,如果自己去拼接和解析报文,确实可以达到效果,不过这样不仅不够优雅并且还十分繁琐,费时费力。查找资料发现 javax 包里有对 xml 的支持。

常用注解

@XmlRootElement
标识这个类或枚举类型是根元素,映射到 XML 元素中。JAXB 中的注解, 作用在类上。

@XmlRootElement(name = "Services")
@XmlAccessorType(XmlAccessType.FIELD)
public class Services {}

@XmlElement
将java对象的属性映射为xml的节点。将没有get方法/set方法的属性映射到XML,作用在字段或方法。

@XmlElement(name = "ServiceInfo", type = ServiceInfo.class)
List<ServiceInfo> ServiceInfo;

@XmlAttribute
將 java 对象的属性映射为 xml 的节点的属性。
例如:将 desc 标识为 < Attr> 节点的属性

@XmlAccessorType(XmlAccessType.FIELD)
public class Attr {

    @XmlAttribute(name = "Desc")
    String desc;

    @XmlElement(name = "AttrName")
    String attrName;

    @XmlElement(name = "AttrCode")
    String attrCode;
}
<Attr Desc="描述">
   <AttrName>材料名称</AttrName>
   <AttrCode>材料编码</AttrCode>
</Attr>

@XmlAccessorType
控制默认情况下是否对字段或 Javabean 属性进行系列化。

默认规则
如果不存在 @XmlAccessorType,使用@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)

可能值:
FIELD: 绑定类中的(每个,没有get方法/set方法的属性也可以)非静态、非瞬态字段将会自动绑定映射到 XML,除非由 XmlTransient 注释。

NONE: 所有字段或属性都不能绑定到 XML,除非使用一些 JAXB 注释专门对它们进行注释。

PROPERTY: 绑定类中的(每个,只有有get方法/set方法的属性才可以)自动绑定映射到 XML,除非由 XmlTransient 注释。

PUBLIC_MEMBER:每个公共获取方法/设置方法对和每个公共字段将会自动绑定到 XML,除非由 XmlTransient 注释。

@XmlTransient(非瞬态)
用于标示在由Java对象映射XML时,忽略此属性,在生成的XML文件中将不出现此元素。

使用案例

以如下 xml 文本为例

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Services>
    <Total>1000</Total>
    <ServiceInfo>
        <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
        <ServiceName>测试</ServiceName>
        <ServiceType>1</ServiceType>
        <State>I</State>
        <PromiseDay>1</PromiseDay>
        <UpdateTime>2023-03-29 08:38:46</UpdateTime>
        <Attrs>
            <Attr>
                <AttrName>材料名称</AttrName>
                <AttrCode>材料编码</AttrCode>
            </Attr>
        </Attrs>
    </ServiceInfo>
    <ServiceInfo>
        <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
        <ServiceName>测试2</ServiceName>
        <ServiceType>1</ServiceType>
        <State>I</State>
        <PromiseDay>1</PromiseDay>
        <UpdateTime>2023-03-29 08:38:46</UpdateTime>
        <Attrs>
            <Attr Desc="描述">
                <AttrName>材料名称</AttrName>
                <AttrCode>材料编码</AttrCode>
            </Attr>
        </Attrs>
    </ServiceInfo>
</Services>

组装实体类

Services.java


import lombok.Data;

import javax.xml.bind.annotation.*;
import java.util.List;

@Data
@XmlRootElement(name = "Services")
@XmlAccessorType(XmlAccessType.FIELD)
public class Services {

    @XmlElement(name = "Total")
    Integer total;

    @XmlElement(name = "ServiceInfo", type = ServiceInfo.class)
    List<ServiceInfo> ServiceInfo;
}

ServiceInfo.java


import lombok.Data;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class ServiceInfo {

    @XmlElement(name = "Unid")
    String uuid;

    @XmlElement(name = "ServiceName")
    String serviceName;

    @XmlElement(name = "Infoprojid")
    String infoprojid;

    @XmlElement(name = "ServiceType")
    String serviceType;

    @XmlElement(name = "State")
    String state;

    @XmlElement(name = "PromiseDay")
    String promiseDay;

    @XmlElement(name = "UpdateTime")
    String updateTime;

    @XmlElement(name = "Attrs", type = Attrs.class)
    Attrs attrs;
}

Attrs.java


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import java.util.List;

@NoArgsConstructor
@AllArgsConstructor
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Attrs {

    @XmlElement(name = "Attr", type = Attr.class)
    List<Attr> attrs;
}

Attr.java


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.xml.bind.annotation.*;

@NoArgsConstructor
@AllArgsConstructor
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Attr {

    @XmlAttribute(name = "Desc")
    String desc;

    @XmlElement(name = "AttrName")
    String attrName;

    @XmlElement(name = "AttrCode")
    String attrCode;
}

工具类


import javax.xml.bind.*;
import java.io.*;

/**
 * jaxb工具类
 */
public class JAXBUtil {

    /**
     * XML转换为POJO类型
     */
    @SuppressWarnings("rawtypes")
    public static Object unmarshall(String xml, Class clsToUnbound) throws JAXBException, UnsupportedEncodingException {
        JAXBContext jc = JAXBContext.newInstance(clsToUnbound);

        return unmarshall(jc, xml);
    }

    private static Object unmarshall(JAXBContext jc, String xml) throws JAXBException, UnsupportedEncodingException {
        Unmarshaller u = jc.createUnmarshaller();
        InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));
        return u.unmarshal(is);
    }

    /**
     * 从流中反序列化对象。
     * 
     * @param cls 需要反序列化的对象类型。
     * @param xmlIs
     *            流对象
     * @return 经过反序列化的对象实例。
     * @throws JAXBException
     */
    public static Object unmarshall(InputStream xmlIs, Class<?> cls) throws JAXBException {
        JAXBContext jc = JAXBContext.newInstance(cls);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Object obj = unmarshaller.unmarshal(xmlIs);
        return obj;
    }

    @SuppressWarnings("unchecked")
    public Object unmarshall(String xml, Class<? extends Object>... classes) throws JAXBException, IOException {
        InputStream is = new ByteArrayInputStream(xml.getBytes());
        JAXBContext jc = JAXBContext.newInstance(classes);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
        Object obj = unmarshaller.unmarshal(is);
        return obj;
    }
    
    /**
     * POJO类型转换为XML
     * @param jc
     * @param serObj
     * @param formatOutput  是否格式化
     * @param fragment 是否隐藏报文头
     * @return
     * @throws JAXBException
     * @throws PropertyException
     */
    private static String marshall(JAXBContext jc, Object serObj, boolean formatOutput, boolean fragment) throws JAXBException, PropertyException {
        StringWriter out = new StringWriter();
        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formatOutput);
        m.setProperty(Marshaller.JAXB_FRAGMENT, fragment);
        m.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
        m.marshal(serObj, out);
        String tmp = out.toString();
        return tmp;
    }
    
    @SuppressWarnings("rawtypes")
    public static String marshall(Object serObj, Class clsToBound) throws JAXBException {
        JAXBContext jc = JAXBContext.newInstance(clsToBound);
        return marshall(jc, serObj, true, false);
    }
    
    public static String marshall(Object serObj, boolean formatOutput, boolean fragment) throws JAXBException {
        JAXBContext jc = JAXBContext.newInstance(serObj.getClass());
        return marshall(jc, serObj, formatOutput, fragment);
    }
    
    public static String marshall(Object serObj, boolean formatOutput) throws JAXBException {
        return marshall(serObj, formatOutput, false);
    }

    /**
     * 将类序列化到流中。
     * 
     * @param contextPath 需要序列化到类名
     * @param obj 需要序列化的实例对象
     * @param stream 需要序列化到的流对象。
     * @throws JAXBException
     */
    public static void marshall(String contextPath, Object obj, OutputStream stream) throws JAXBException {
        JAXBContext jc = JAXBContext.newInstance(contextPath);
        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        m.marshal(obj, stream);
    }

测试


public class JAXBUsage {
    public static void main(String[] args) {
        String xmlstr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                "\n" +
                "<Services> \n" +
                "  <Total>1000</Total>  \n" +
                "  <ServiceInfo> \n" +
                "    <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>  \n" +
                "    <ServiceName><![CDATA[测试]]></ServiceName>  \n" +
                "    <ServiceType>1</ServiceType>  \n" +
                "    <State>I</State>  \n" +
                "    <PromiseDay>1</PromiseDay>  \n" +
                "    <UpdateTime>2019-01-11 08:38:46</UpdateTime>  \n" +
                "    <Attrs> \n" +
                "      <Attr> \n" +
                "        <AttrName><![CDATA[材料名称]]></AttrName>  \n" +
                "        <AttrCode>材料编码</AttrCode> \n" +
                "      </Attr> \n" +
                "    </Attrs> \n" +
                "  </ServiceInfo>  \n" +
                "  <ServiceInfo> \n" +
                "    <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>  \n" +
                "    <ServiceName><![CDATA[测试2]]></ServiceName>  \n" +
                "    <ServiceType>1</ServiceType>  \n" +
                "    <State>I</State>  \n" +
                "    <PromiseDay>1</PromiseDay>  \n" +
                "    <UpdateTime>2019-01-11 08:38:46</UpdateTime>  \n" +
                "    <Attrs> \n" +
                "      <Attr Desc = '描述'> \n" +
                "        <AttrName><![CDATA[材料名称]]></AttrName>  \n" +
                "        <AttrCode>材料编码</AttrCode> \n" +
                "      </Attr> \n" +
                "    </Attrs> \n" +
                "  </ServiceInfo> \n" +
                "</Services>\n";

        try{
            //xmlStr To pojo
            Services services = (Services)JAXBUtil.unmarshall(xmlstr, Services.class);
            System.out.println(services.toString());

            //pojo To xmlStr
            String s = JAXBUtil.marshall(services, Services.class);
            System.out.println(s);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

测试结果

Services(total=1000, ServiceInfo=[ServiceInfo(uuid=7B31FB4508BE82F2E6FB761E067E60CF, serviceName=测试, infoprojid=null, serviceType=1, state=I, promiseDay=1, updateTime=2019-01-11 08:38:46, attrs=Attrs(attrs=[Attr(desc=null, attrName=材料名称, attrCode=材料编码)])), ServiceInfo(uuid=7B31FB4508BE82F2E6FB761E067E60CF, serviceName=测试2, infoprojid=null, serviceType=1, state=I, promiseDay=1, updateTime=2019-01-11 08:38:46, attrs=Attrs(attrs=[Attr(desc=描述, attrName=材料名称, attrCode=材料编码)]))])
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Services>
    <Total>1000</Total>
    <ServiceInfo>
        <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
        <ServiceName>测试</ServiceName>
        <ServiceType>1</ServiceType>
        <State>I</State>
        <PromiseDay>1</PromiseDay>
        <UpdateTime>2019-01-11 08:38:46</UpdateTime>
        <Attrs>
            <Attr>
                <AttrName>材料名称</AttrName>
                <AttrCode>材料编码</AttrCode>
            </Attr>
        </Attrs>
    </ServiceInfo>
    <ServiceInfo>
        <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
        <ServiceName>测试2</ServiceName>
        <ServiceType>1</ServiceType>
        <State>I</State>
        <PromiseDay>1</PromiseDay>
        <UpdateTime>2019-01-11 08:38:46</UpdateTime>
        <Attrs>
            <Attr Desc="描述">
                <AttrName>材料名称</AttrName>
                <AttrCode>材料编码</AttrCode>
            </Attr>
        </Attrs>
    </ServiceInfo>
</Services>

注意

1、组装对象层级需遵循 xml 层级, 对象属性可以与 xml 不一致,但注解里的 name 属性需与xml 保持一致。

2、实体对象如果没有初始化值,转 xml 时会丢失该属性。如果属性不能丢失,那么则需要将其初始化,如果是集合则初始化为空集合。

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

推荐阅读更多精彩内容