java JAXB XML 适配器通过注解工作,但不通过 setAdapter

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6110757/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 14:22:21  来源:igfitidea点击:

JAXB XML Adapters work via annotations but not via setAdapter

javaxmljaxbmarshalling

提问by I82Much

I understand all about how to use XMLAdaptersto convert unmappable types, or just to change how certain objects are serialized/deserialized to XML. Everything works great if I use annotations (either package level or otherwise). The problem is that I am attempting to change the representations of third party objects which I cannot change the source code to (i.e. in order to inject annotations).

我完全了解如何使用XMLAdapters转换不可映射的类型,或者只是改变某些对象如何序列化/反序列化为 XML。如果我使用注释(包级别或其他级别),一切都会很好。问题是我试图更改我无法将源代码更改为的第三方对象的表示(即为了注入注释)。

That shouldn't be a problem, considering that the Marshaller object has a method for manually adding adapters. Unfortunately, no matter what I do, I can't get the adapters set in this way to "kick in". For instance, I have a class representing a point in XYZ space (geocentric coordinates). In the XML I produce, I want this to be converted into lat/long/altitude (geodetic coordinates). Here are my classes:

考虑到 Marshaller 对象具有手动添加适配器的方法,这应该不是问题。不幸的是,无论我做什么,我都无法以这种方式设置适配器以“启动”。例如,我有一个类表示 XYZ 空间(地心坐标)中的一个点。在我生成的 XML 中,我希望将其转换为纬度/经度/高度(大地坐标)。这是我的课程:

Geocentric

地心说

package testJaxb;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class GeocentricCoordinate {
    // Units are in meters; see http://en.wikipedia.org/wiki/Geocentric_coordinates
    private double x;
    private double y;
    private double z;

    @XmlAttribute
    public double getX() {
        return x;
    }
    public void setX(double x) {
        this.x = x;
    }
    @XmlAttribute
    public double getY() {
        return y;
    }
    public void setY(double y) {
        this.y = y;
    }
    @XmlAttribute
    public double getZ() {
        return z;
    }
    public void setZ(double z) {
        this.z = z;
    }
}

Geodetic

大地测量

package testJaxb;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
/**
 * @see http://en.wikipedia.org/wiki/Geodetic_system
 */
@XmlRootElement
public class GeodeticCoordinate {

    private double latitude;
    private double longitude;
    // Meters
    private double altitude;

    public GeodeticCoordinate() {
        this(0,0,0);
    }

    public GeodeticCoordinate(double latitude, double longitude, double altitude) {
        super();
        this.latitude = latitude;
        this.longitude = longitude;
        this.altitude = altitude;
    }

    @XmlAttribute
    public double getLatitude() {
        return latitude;
    }
    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    @XmlAttribute
    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    @XmlAttribute
    public double getAltitude() {
        return altitude;
    }
    public void setAltitude(double altitude) {
        this.altitude = altitude;
    }



}

GeocentricToGeodeticLocationAdapter

地心到大地定位适配器

package testJaxb;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.adapters.XmlAdapter;


/**
 * One of our systems uses xyz coordinates to represent locations. Consumers of our XML would much
 * prefer lat/lon/altitude.  This handles converting between xyz and lat lon alt.  
 * 
 * @author ndunn
 *
 */
public class GeocentricToGeodeticLocationAdapter extends XmlAdapter<GeodeticCoordinate,GeocentricCoordinate> {

    @Override
    public GeodeticCoordinate marshal(GeocentricCoordinate arg0) throws Exception {
        // TODO: do a real coordinate transformation
        GeodeticCoordinate coordinate = new GeodeticCoordinate();
        coordinate.setLatitude(45);
        coordinate.setLongitude(45);
        coordinate.setAltitude(1000);
        return coordinate;
    }

    @Override
    public GeocentricCoordinate unmarshal(GeodeticCoordinate arg0) throws Exception {
        // TODO do a real coordinate transformation
        GeocentricCoordinate gcc = new GeocentricCoordinate();
        gcc.setX(100);
        gcc.setY(200);
        gcc.setZ(300);
        return gcc;
    }
}

ObjectWithLocation field

ObjectWithLocation 字段

package testJaxb; 
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class ObjectWithLocation {

    private GeocentricCoordinate location = new GeocentricCoordinate();

    public GeocentricCoordinate getLocation() {
        return location;
    }

    public void setLocation(GeocentricCoordinate location) {
        this.location = location;
    }


    public static void main(String[] args) {

        ObjectWithLocation object = new ObjectWithLocation();

        try {
            JAXBContext context = JAXBContext.newInstance(ObjectWithLocation.class, GeodeticCoordinate.class, GeocentricCoordinate.class);
            Marshaller marshaller = context.createMarshaller();

            marshaller.setAdapter(new GeocentricToGeodeticLocationAdapter());
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            marshaller.marshal(object, System.out);

        }
        catch (JAXBException jaxb) {
            jaxb.printStackTrace();
        }
    }
}

Output:

输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithLocation>
    <location z="0.0" y="0.0" x="0.0"/>
</objectWithLocation>

By using an annotation (in my package-info.javafile):

通过使用注释(在我的package-info.java文件中):

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters
({
@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=GeocentricToGeodeticLocationAdapter.class,type=GeocentricCoordinate.class),
})

package package testJaxb;

I get the following (desired) xml

我得到以下(所需的)xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithLocation>
    <location longitude="45.0" latitude="45.0" altitude="1000.0"/>
</objectWithLocation>

So my question is twofold.

所以我的问题是双重的。

  1. Why does the adapter work when annotated, but not when explicitly set via the setAdaptermethod?
  2. How do I work around this problem when I have classes that I cannot annotate and whose package-info.java I cannot modify in order to add the annotations?
  1. 为什么适配器在注释时工作,但在通过setAdapter方法显式设置时不起作用?
  2. 当我有无法注释的类以及无法修改其 package-info.java 以添加注释时,如何解决此问题?

回答by bdoughan

The setAdapter(XmlAdapter)on Marshalleris used to pass in an initialized XmlAdapter for a property that is already annotated with @XmlJavaTypeAdapter. The link below is to an answer where I leverage this behaviour:

setAdapter(XmlAdapter)Marshaller使用在初始化XmlAdapter通过对已与注释的属性@XmlJavaTypeAdapter。下面的链接是我利用这种行为的答案:

If you want to map third party classes you could use EclipseLink JAXB (MOXy)'s XML mapping file (I'm the MOXy lead):

如果您想映射第三方类,您可以使用EclipseLink JAXB (MOXy)的 XML 映射文件(我是 MOXy 的负责人):

回答by user1697575

You always have to annotate with @XmlJavaTypeAdapter(...).

你总是必须注释 @XmlJavaTypeAdapter(...).

marshaller.setAdapter(...)is means to assign a custom initialized instance of your type adapter in case you have non default constructor initialisation.

marshaller.setAdapter(...)是指分配类型适配器的自定义初始化实例,以防您有非默认构造函数初始化。

Otherwise, if you have only one default constructor for your adapter, then you don't need to explicitly call .setAdapter(...)method.

否则,如果您的适配器只有一个默认构造函数,则不需要显式调用.setAdapter(...)方法。

Here is a great answer with more detailed explanation: JAXB: Isn't it possible to use an XmlAdapter without @XmlJavaTypeAdapter?

这是一个带有更详细解释的很好的答案: JAXB:是否可以在没有 @XmlJavaTypeAdapter 的情况下使用 XmlAdapter?

JAXB Runtime can only accept Adapter with No-args constructor .. (Obviously JAXBContextdoes not know about application specific model)

JAXB 运行时只能接受带有无参数构造函数的适配器..(显然JAXBContext不知道应用程序特定模型)

So thankfully there is an option :D

谢天谢地,有一个选择:D

You can tell your unmarshaller to use given instance of UserAdapter rather than instating it by own its own.

您可以告诉您的解组器使用 UserAdapter 的给定实例,而不是自行安装它。

public class Test {
public static void main(String... args) { 
    JAXBContext context = JAXBContext.getInstance(Event.class);
    Unmarshaller unmarshaller = context.createUnmarshaller();

      UserContext userContext = null; // fetch it from some where
      unmarshaller.setAdapter(UserAdapter.class, new UserAdapter(userContext));

      Event event = (Event) unmarshaller.unmarshal(..);
   }
}

setAdaptermethod is available on both Marshaller & Unmarshaller

setAdapter方法在 Marshaller 和 Unmarshaller 上都可用

Note: setAdapter on marshaller / unmarshaller does not mean that you don't have to use @XmlJavaTypeAdapter.

注意: marshaller / unmarshaller 上的 setAdapter 并不意味着您不必使用@XmlJavaTypeAdapter.