如何正确使用XmlAdapter?

编程语言 2026-07-12

最近我需要把Java对象转换为XML文件格式,但直接使用JAXB转换Java对象并不能满足我的需求。

通过研究,我发现XmlAdapter可以用于自定义XML,但我不知道如何正确使用XmlAdapter来实现我的需求。

我期望的效果:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <head>
        <a>a</a>
        <b>b</b>
        <c>c</c>
    </head>
    <body>
        <d>d</d>
        <e>e</e>
    </body>
</root>

但像这样运行后的效果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <a>a</a>
    <b>b</b>
    <c>c</c>
    <d>d</d>
    <e>e</e>
</root>

以下是我的源代码,使用Java 8。

以下是我的目标类。

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "root")
public class MiddleRoot {

    private Head head;
    private Body body;

    public MiddleRoot() {
    }

    public MiddleRoot(Head head, Body body) {
        this.head = head;
        this.body = body;
    }


    public Head getHead() {
        return head;
    }


    public void setHead(Head head) {
        this.head = head;
    }


    public Body getBody() {
        return body;
    }


    public void setBody(Body body) {
        this.body = body;
    }


    static class Head{

        private String a;

        private String b;

        private String c;


        public Head() {
        }

        public Head(String a, String b, String c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }


        public String getA() {
            return a;
        }


        public void setA(String a) {
            this.a = a;
        }


        public String getB() {
            return b;
        }

        public void setB(String b) {
            this.b = b;
        }


        public String getC() {
            return c;
        }


        public void setC(String c) {
            this.c = c;
        }

    }


    static class Body{

        private String d;

        private String e;


        public Body() {
        }

        public Body(String d, String e) {
            this.d = d;
            this.e = e;
        }


        public String getD() {
            return d;
        }


        public void setD(String d) {
            this.d = d;
        }


        public String getE() {
            return e;
        }

        public void setE(String e) {
            this.e = e;
        }

    }


}

以下是我的原始类。

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement(name = "root")
@XmlJavaTypeAdapter(RootAdapter.class)
public class Root {


    private String a;

    private String b;

    private String c;

    private String d;

    private String e;


    public Root() {
    }

    public Root(String a, String b, String c, String d, String e) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
    }

    public String getA() {
        return a;
    }


    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }


    public void setB(String b) {
        this.b = b;
    }


    public String getC() {
        return c;
    }


    public void setC(String c) {
        this.c = c;
    }


    public String getD() {
        return d;
    }


    public void setD(String d) {
        this.d = d;
    }


    public String getE() {
        return e;
    }

    public void setE(String e) {
        this.e = e;
    }


}

以下是我的适配器类。

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

public class RootAdapter extends XmlAdapter<MiddleRoot,Root> {

    @Override
    public Root unmarshal(MiddleRoot v) throws Exception {

        MiddleRoot.Head head = v.getHead();
        MiddleRoot.Body body = v.getBody();

        Root r = new Root();

        if(head!=null){
            r.setA(head.getA());
            r.setB(head.getB());
            r.setC(head.getC());
        }

        if(body!=null){
            r.setD(body.getD());
            r.setE(body.getE());
        }

        return r;
    }

    @Override
    public MiddleRoot marshal(Root v) throws Exception {
        MiddleRoot.Head head = new MiddleRoot.Head(v.getA(), v.getB(), v.getC());
        MiddleRoot.Body body = new MiddleRoot.Body(v.getD(), v.getE());
        return new MiddleRoot(head,body);
    }
}

以下是我的 Test 类。

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class Test {

    public static void main(String[] args) {

        try {
            JAXBContext context = JAXBContext.newInstance(Root.class);
            Root r = new Root("a","b","c","d","e");

            Marshaller marshaller = context.createMarshaller();


            marshaller.setProperty(Marshaller.JAXB_ENCODING,"UTF-8");
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);


            marshaller.marshal(r,System.out);


        } catch (JAXBException e) {
            System.out.println(e.getMessage());
        }


    }

}

这四个类都在同一个包中。如果我删除Root类上的 XmlRootElement,在运行时它会返回 null

解决方案

问题在于你的 Root 类上有冲突的注解。
当你在同一个类上同时应用 @XmlRootElement@XmlJavaTypeAdapter 时,JAXB不知道该走哪条路。@XmlRootElement 告诉JAXB “将这个类直接序列化为XML”,但适配器表示要先把这个类转换成其他形式。JAXB会变得困惑,选择了错误的路径。

首先:从Root类中移除 @XmlRootElement

@XmlJavaTypeAdapter(RootAdapter.class) 
public class Root {
    // everything else stays the same
}

然后更新你的JAXBContext,使其同时包含这两个类。

JAXBContext.newInstance(Root.class, MiddleRoot.class);

然后当JAXB看到你的Root对象时,它将调用你的适配器把 Root 转换为 MiddleRoot

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章