为什么我不能将这个简单的对象映射到 Java/Jersey 中的 XML 中的文本?
Why can't I map this simple object to text in XML in Java/Jersey?
我在 Java 中使用 Jersey 创建了一个 REST API。
对于一个请求,我想 return in JSON 一对坐标的元组列表。
为此,我有一个 class,它是 ArrayList
、Tuple2
class 和 Coords
class 的包装器。
我使用 javax.xml.bind.annotations
自动生成 classes 的 XML/JSON。
但是由于我不明白我的 Coords
class 的原因,我无法映射到 XML。
我尝试了不同类型的属性(Integers
而不是 int
),@XmlAttribute
在不同的地方(在属性之前和 getter 之前)和不同的 XmlAccessType
(PROPERTY
而不是 NONE
),但结果是一样的。
这是我的坐标 class :
package model;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
@XmlRootElement
@XmlAccessorType(NONE)
public class Coords {
@XmlAttribute private int x;
@XmlAttribute private int y;
public Coords(final int x, final int y) {
this.x = x;
this.y = y;
}
public Coords() {
this.x = 0;
this.y = 0;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
}
下面是它在我的 Tuple2 中的显示方式
package model;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
// ...
@XmlAttribute
public Coords getFirst() {
return this.c1;
}
@XmlAttribute
public Coords getSecond() {
return this.c2;
}
// ...
}
错误信息如下:
[EL Warning]: moxy: 2019-10-27 15:01:08.586--javax.xml.bind.JAXBException:
Exception Description: The @XmlAttribute property first in type model.Tuple2 must reference a type that maps to text in XML. model.Coords cannot be mapped to a text value.
- with linked exception:
[Exception [EclipseLink-50096] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.JAXBException
Exception Description: The @XmlAttribute property first in type model.Tuple2 must reference a type that maps to text in XML. model.Coords cannot be mapped to a text value.]
oct. 27, 2019 3:01:08 PM org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
GRAVE: MessageBodyWriter not found for media type=application/json, type=class model.ActionList, genericType=class model.ActionList.
感谢您的帮助。
您的问题来自 xml 注释的误用。您通过使用 @XmlRootElement
注释将 Tuple2
定义为 xml 根元素,通过使用 [=19= 注释 get 方法将其字段定义为 xml 属性].转化为:
<tuple2 first="first_attributes_vale" second="second_attributes_value" />
现在,两个字段都是 Coords
类型,通过将 Coords
class 注释为 @XmlRootElement
,声明为另一个 xml 元素,及其字段为 xml 属性。当 Coords
序列化为 xml 时,它将是:
<coords x="value" y="value" />
序列化 Tuple2
时出现问题。它的字段应该是 xml 属性,但 Coords
是另一个 xml 元素。 Xml 属性不能包含嵌套元素,只能包含值。
解决方案
根据您的需要,您可以通过两种不同的方式解决此问题。虽然,我不推荐第二种方法,因为它很奇怪(即使它有效)并且会在客户端产生额外的工作(见下面的解释)。
第一种方法
使用 @XmlElement
注释对 getFirst()
和 getSecond()
方法进行注释。
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
public Tuple2(Coords c1, Coords c2) {
this.c1 = c1;
this.c2 = c2;
}
public Tuple2() {
c1 = new Coords(0, 0);
c2 = new Coords(0, 0);
}
@XmlElement
public Coords getFirst() {
return this.c1;
}
@XmlElement
public Coords getSecond() {
return this.c2;
}
}
这将产生如下所示的结果:
<tuple2>
<first x="2" y="4"/>
<second x="12" y="12"/>
</tuple2>
第二种方法
这是一种奇怪的解决方法。它有效,但它会在客户端产生额外的工作量,因为 Coords
的值被编码为字符串值,需要在接收端进行解析。
将 getFirst()
和 getSecond()
方法的 return 类型更改为 String
并覆盖 Coords
的 toString()
方法。
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
public Tuple2(Coords c1, Coords c2) {
this.c1 = c1;
this.c2 = c2;
}
public Tuple2() {
c1 = new Coords(0, 0);
c2 = new Coords(0, 0);
}
@XmlAttribute
public String getFirst() {
return this.c1.toString();
}
@XmlAttribute
public String getSecond() {
return this.c2.toString();
}
}
覆盖Coords
的toString()
方法:
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Coords {
@XmlAttribute private int x;
@XmlAttribute private int y;
public Coords(final int x, final int y) {
this.x = x;
this.y = y;
}
public Coords() {
this.x = 0;
this.y = 0;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Coords [x=");
builder.append(x);
builder.append(", y=");
builder.append(y);
builder.append("]");
return builder.toString();
}
}
这将产生与此类似的结果:
<tuple2 first="Coords [x=2, y=4]" second="Coords [x=12, y=12]"/>
属性 first
和 second
的值将是 Coords
return 的 toString()
方法的任何值。
我在 Java 中使用 Jersey 创建了一个 REST API。
对于一个请求,我想 return in JSON 一对坐标的元组列表。
为此,我有一个 class,它是 ArrayList
、Tuple2
class 和 Coords
class 的包装器。
我使用 javax.xml.bind.annotations
自动生成 classes 的 XML/JSON。
但是由于我不明白我的 Coords
class 的原因,我无法映射到 XML。
我尝试了不同类型的属性(Integers
而不是 int
),@XmlAttribute
在不同的地方(在属性之前和 getter 之前)和不同的 XmlAccessType
(PROPERTY
而不是 NONE
),但结果是一样的。
这是我的坐标 class :
package model;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
@XmlRootElement
@XmlAccessorType(NONE)
public class Coords {
@XmlAttribute private int x;
@XmlAttribute private int y;
public Coords(final int x, final int y) {
this.x = x;
this.y = y;
}
public Coords() {
this.x = 0;
this.y = 0;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
}
下面是它在我的 Tuple2 中的显示方式
package model;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
// ...
@XmlAttribute
public Coords getFirst() {
return this.c1;
}
@XmlAttribute
public Coords getSecond() {
return this.c2;
}
// ...
}
错误信息如下:
[EL Warning]: moxy: 2019-10-27 15:01:08.586--javax.xml.bind.JAXBException:
Exception Description: The @XmlAttribute property first in type model.Tuple2 must reference a type that maps to text in XML. model.Coords cannot be mapped to a text value.
- with linked exception:
[Exception [EclipseLink-50096] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.JAXBException
Exception Description: The @XmlAttribute property first in type model.Tuple2 must reference a type that maps to text in XML. model.Coords cannot be mapped to a text value.]
oct. 27, 2019 3:01:08 PM org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
GRAVE: MessageBodyWriter not found for media type=application/json, type=class model.ActionList, genericType=class model.ActionList.
感谢您的帮助。
您的问题来自 xml 注释的误用。您通过使用 @XmlRootElement
注释将 Tuple2
定义为 xml 根元素,通过使用 [=19= 注释 get 方法将其字段定义为 xml 属性].转化为:
<tuple2 first="first_attributes_vale" second="second_attributes_value" />
现在,两个字段都是 Coords
类型,通过将 Coords
class 注释为 @XmlRootElement
,声明为另一个 xml 元素,及其字段为 xml 属性。当 Coords
序列化为 xml 时,它将是:
<coords x="value" y="value" />
序列化 Tuple2
时出现问题。它的字段应该是 xml 属性,但 Coords
是另一个 xml 元素。 Xml 属性不能包含嵌套元素,只能包含值。
解决方案
根据您的需要,您可以通过两种不同的方式解决此问题。虽然,我不推荐第二种方法,因为它很奇怪(即使它有效)并且会在客户端产生额外的工作(见下面的解释)。
第一种方法
使用 @XmlElement
注释对 getFirst()
和 getSecond()
方法进行注释。
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
public Tuple2(Coords c1, Coords c2) {
this.c1 = c1;
this.c2 = c2;
}
public Tuple2() {
c1 = new Coords(0, 0);
c2 = new Coords(0, 0);
}
@XmlElement
public Coords getFirst() {
return this.c1;
}
@XmlElement
public Coords getSecond() {
return this.c2;
}
}
这将产生如下所示的结果:
<tuple2>
<first x="2" y="4"/>
<second x="12" y="12"/>
</tuple2>
第二种方法
这是一种奇怪的解决方法。它有效,但它会在客户端产生额外的工作量,因为 Coords
的值被编码为字符串值,需要在接收端进行解析。
将 getFirst()
和 getSecond()
方法的 return 类型更改为 String
并覆盖 Coords
的 toString()
方法。
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
public Tuple2(Coords c1, Coords c2) {
this.c1 = c1;
this.c2 = c2;
}
public Tuple2() {
c1 = new Coords(0, 0);
c2 = new Coords(0, 0);
}
@XmlAttribute
public String getFirst() {
return this.c1.toString();
}
@XmlAttribute
public String getSecond() {
return this.c2.toString();
}
}
覆盖Coords
的toString()
方法:
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Coords {
@XmlAttribute private int x;
@XmlAttribute private int y;
public Coords(final int x, final int y) {
this.x = x;
this.y = y;
}
public Coords() {
this.x = 0;
this.y = 0;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Coords [x=");
builder.append(x);
builder.append(", y=");
builder.append(y);
builder.append("]");
return builder.toString();
}
}
这将产生与此类似的结果:
<tuple2 first="Coords [x=2, y=4]" second="Coords [x=12, y=12]"/>
属性 first
和 second
的值将是 Coords
return 的 toString()
方法的任何值。