JAXB處理XML與對象互轉

一、簡介

  • 本文主要講解JAXB來處理對象與XML進行互轉。
  • 其中關於CDATA數據的處理參考: Jaxb如何優雅的處理CData 這篇文章的實現,並略作改動。

二、常用註解(TODO)

三、封裝

3.1 pom依賴(TODO)

 

3.2 CDataAdapter

package com.zhenai.sweet.qywx.provider.util;

import javax.xml.bind.annotation.adapters.XmlAdapter;

/**
 * JAXB的CDATA轉換處理類
 */
public class CDataAdapter extends XmlAdapter<String, String> {

    @Override
    public String unmarshal(String v) throws Exception {
        return v;
    }

    @Override
    public String marshal(String v) throws Exception {
        return new StringBuilder("<![CDATA[").append(v).append("]]>").toString();
    }

}

3.3 CDataHandler

package com.zhenai.sweet.qywx.provider.util;

import javax.xml.stream.XMLStreamWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 動態代理處理
 */
public class CDataHandler implements InvocationHandler {

    private XMLStreamWriter writer;

    public CDataHandler(XMLStreamWriter writer) {
        this.writer = writer;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Method gWriteCharactersMethod = null;
        try {
            gWriteCharactersMethod = XMLStreamWriter.class.getDeclaredMethod("writeCharacters", String.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        if (gWriteCharactersMethod.equals(method)) {
            String text = (String) args[0];
            if (text != null && text.startsWith("<![CDATA[") && text.endsWith("]]>")) {
                writer.writeCData(text.substring(9, text.length() - 3));
                return null;
            }
        }
        return method.invoke(writer, args);
    }

}

 

3.4 JaxbXmlUtil

package com.zhenai.sweet.qywx.provider.util;

import lombok.extern.slf4j.Slf4j;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;


/**
 * @Description Jaxb工具類 xml和java類相互轉換
 * @Author: qjwyss
 * @Date: 2020/3/13 16:59
 * @Version V1.0
 */
@Slf4j
public class JaxbXmlUtil {

    private static final String DEFAULT_ENCODING = "UTF-8";

    /**
     * pojo轉換成xml 默認編碼UTF-8
     *
     * @param obj 待轉化的對象
     * @return xml格式字符串
     * @throws Exception JAXBException
     */
    public static String pojo2xml(Object obj) throws Exception {
        return pojo2xml(obj, DEFAULT_ENCODING);
    }


    /**
     * pojo轉換成xml
     *
     * @param obj      待轉化的對象
     * @param encoding 編碼
     * @return xml格式字符串
     * @throws Exception JAXBException
     */
    public static String pojo2xml(Object obj, String encoding) throws Exception {
        String result = null;

        JAXBContext context = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = context.createMarshaller();
        // 指定是否使用換行和縮排對已編組 XML 數據進行格式化的屬性名稱。
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);

        try (StringWriter writer = new StringWriter();) {
            XMLStreamWriter streamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer);
            XMLStreamWriter cdataStreamWriter = (XMLStreamWriter) Proxy.newProxyInstance(
                    streamWriter.getClass().getClassLoader(),
                    streamWriter.getClass().getInterfaces(),
                    new CDataHandler(streamWriter)
            );
            marshaller.marshal(obj, cdataStreamWriter);
            result = formatXml(writer.toString());
        } catch (Exception e) {
            log.error("讀取數據失敗", e);
        }

        return result;
    }


    /**
     * xml轉換成pojo
     *
     * @param xml xml格式字符串
     * @param t   待轉化的對象
     * @return 轉化後的對象
     * @throws Exception JAXBException
     */
    @SuppressWarnings("unchecked")
    public static <T> T xml2pojo(String xml, Class<T> t) throws Exception {
        T obj;
        JAXBContext context = JAXBContext.newInstance(t);
        Unmarshaller unmarshaller = context.createUnmarshaller();

        try (StringReader stringReader = new StringReader(xml)) {
            obj = (T) unmarshaller.unmarshal(stringReader);
        }
        return obj;
    }


    /**
     * xml文本對齊
     */
    private static String formatXml(String xml) {
        try {
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            // 打開對齊開關
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            // 忽略掉xml聲明頭信息
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

            StringWriter formattedStringWriter = new StringWriter();
            transformer.transform(new StreamSource(new StringReader(xml)),
                    new StreamResult(formattedStringWriter));

            return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                    + formattedStringWriter.toString();
        } catch (TransformerException e) {
        }
        return null;
    }


}

 

四、使用

4.1 簡單對象互轉

4.1.0 說明

  • 此處僅使用一個簡單User對象來進行轉換;

4.1.1 User對象

package com.zhenai.sweet.qywx.provider.util;

import lombok.Data;

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

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "user")
@XmlType(propOrder = {
        "uid",
        "username",
        "password"
})
public class User implements Serializable {

    private static final long serialVersionUID = -4476533660865890728L;

    private Long uid;

    private String username;

    private String password;

    @XmlAttribute
    private String gender;

    public User() {
    }

    public User(Long uid, String username, String password, String gender) {
        this.uid = uid;
        this.username = username;
        this.password = password;
        this.gender = gender;
    }
}

4.1.2 使用

// 簡單對象互轉
User user = new User(1L, "小明", "123456", "男");
String userXml = JaxbXmlUtil.pojo2xml(user);
log.info("簡單對象轉換XML結果: {}", userXml);
user = JaxbXmlUtil.xml2pojo(userXml, User.class);
log.info("簡單XML轉換對象結果: {}", user.toString());

4.1.3 結果

19:31:57.023 [main] INFO com.zhenai.sweet.qywx.provider.util.JaxbXmlUtil - 簡單對象轉換XML結果: <?xml version="1.0" encoding="UTF-8"?>
<user gender="男">
    <uid>1</uid>
    <username>小明</username>
    <password>123456</password>
</user>

19:31:57.035 [main] INFO com.zhenai.sweet.qywx.provider.util.JaxbXmlUtil - 簡單XML轉換對象結果: User(uid=1, username=小明, password=123456, gender=男)

 

4.2 複雜對象互轉

4.2.0 說明

  • 此處通過設置嵌套對象來轉換複雜的xml
  • 有三個對象,班級對象,老師對象,學生對象; 一個班級對應一個老師,一個班級對應一羣學生;

4.2.1 對象

package com.zhenai.sweet.qywx.provider.util;

import lombok.Data;

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

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "banji")
@XmlType(propOrder = {
        "classId",
        "className",
        "teacher",
        "studentList"
})
public class Banji implements Serializable {

    private static final long serialVersionUID = -7645165684680423691L;

    private Long classId;

    private String className;

    private Teacher teacher;

    @XmlElementWrapper
    @XmlElement(name = "student")
    private List<Student> studentList;

    public Banji() {
    }

    public Banji(Long classId, String className, Teacher teacher, List<Student> studentList) {
        this.classId = classId;
        this.className = className;
        this.teacher = teacher;
        this.studentList = studentList;
    }
}
package com.zhenai.sweet.qywx.provider.util;

import lombok.Data;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "teacher")
@XmlType(propOrder = {
        "teacherName",
        "teacherGender",
})
public class Teacher implements Serializable {

    private static final long serialVersionUID = 9190597564351664551L;

    private String teacherName;
    private String teacherGender;

    public Teacher() {
    }

    public Teacher(String teacherName, String teacherGender) {
        this.teacherName = teacherName;
        this.teacherGender = teacherGender;
    }
}

 

package com.zhenai.sweet.qywx.provider.util;

import lombok.Data;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "student")
@XmlType(propOrder = {
        "studentName",
        "age",
})
public class Student implements Serializable {

    private static final long serialVersionUID = -7099299365239347614L;

    private String studentName;
    private Integer age;

    public Student() {
    }

    public Student(String studentName, Integer age) {
        this.studentName = studentName;
        this.age = age;
    }
}

 

4.2.2 使用

// 複合對象互轉
Teacher teacher = new Teacher("語文老師", "女");
List<Student> studentList = new ArrayList<>();
Student student1 = new Student("小雪", 15);
Student student2 = new Student("小花", 13);
Student student3 = new Student("小李", 14);
studentList.add(student1);
studentList.add(student2);
studentList.add(student3);
Banji banji = new Banji(2L, "三年一班", teacher, studentList);
String banjiXml = JaxbXmlUtil.pojo2xml(banji);
log.info("複合對象轉XML:{}", banjiXml);
banji = JaxbXmlUtil.xml2pojo(banjiXml, Banji.class);
log.info("複合XML轉對象結果:{}", banji.toString());

 

4.2.3 結果

19:35:42.168 [main] INFO com.zhenai.sweet.qywx.provider.util.JaxbXmlUtil - 複合對象轉XML:<?xml version="1.0" encoding="UTF-8"?>
<banji>
    <classId>2</classId>
    <className>三年一班</className>
    <teacher>
        <teacherName>語文老師</teacherName>
        <teacherGender>女</teacherGender>
    </teacher>
    <studentList>
        <student>
            <studentName>小雪</studentName>
            <age>15</age>
        </student>
        <student>
            <studentName>小花</studentName>
            <age>13</age>
        </student>
        <student>
            <studentName>小李</studentName>
            <age>14</age>
        </student>
    </studentList>
</banji>

19:35:42.171 [main] INFO com.zhenai.sweet.qywx.provider.util.JaxbXmlUtil - 複合XML轉對象結果:Banji(classId=2, className=三年一班, teacher=Teacher(teacherName=語文老師, teacherGender=女), studentList=[Student(studentName=小雪, age=15), Student(studentName=小花, age=13), Student(studentName=小李, age=14)])

 

4.3 CDATA使用

4.3.0 說明

  • 僅需要在需要CDATA的字段上加上註解@XmlJavaTypeAdapter(CDataAdapter.class)即可;

4.3.1 User對象

package com.zhenai.sweet.qywx.provider.util;

import lombok.Data;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "user")
@XmlType(propOrder = {
        "uid",
        "username",
        "password"
})
public class User implements Serializable {

    private static final long serialVersionUID = -4476533660865890728L;

    private Long uid;

    @XmlJavaTypeAdapter(CDataAdapter.class)
    private String username;

    private String password;

    @XmlAttribute
    private String gender;

    public User() {
    }

    public User(Long uid, String username, String password, String gender) {
        this.uid = uid;
        this.username = username;
        this.password = password;
        this.gender = gender;
    }
}

4.3.2 結果

19:40:34.177 [main] INFO com.zhenai.sweet.qywx.provider.util.JaxbXmlUtil - 簡單對象轉換XML結果: <?xml version="1.0" encoding="UTF-8"?>
<user gender="男">
    <uid>1</uid>
    <username><![CDATA[小明]]></username>
    <password>123456</password>
</user>

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章