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
JAXB XML Adapters work via annotations but not via setAdapter
提问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.java
file):
通过使用注释(在我的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.
所以我的问题是双重的。
- Why does the adapter work when annotated, but not when explicitly set via the
setAdapter
method? - 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?
- 为什么适配器在注释时工作,但在通过
setAdapter
方法显式设置时不起作用? - 当我有无法注释的类以及无法修改其 package-info.java 以添加注释时,如何解决此问题?
回答by bdoughan
The setAdapter(XmlAdapter)
on Marshaller
is 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 JAXBContext
does 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(..);
}
}
setAdapter
method 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
.