可选且可空的 JAXB 元素
我已重新格式化问题,希望能更清楚地表达我的意图.
I have re-formatted the question to hopefully make my intentions clearer.
架构
我正在编写一些 Web 服务,我将使用 JAX-WS 自己发布这些服务.我们已经使用了一段时间的过程是首先编写一个只定义请求和响应对象的模式.这将发送给客户以批准 xml 消息的结构.我不想自己编写整个 wsdl,因为它比基本架构更复杂.
Architecture
I'm writing some web services that I will be publishing myself using JAX-WS. The process we have been using for some time is to first write a schema that only defines the request and response objects. This gets sent to the customer to approve the structure of the xml messages. I don't want to write the whole wsdl myself as it's more complicated than the basic schema.
接下来,我使用 JAXB 命令 xjc 根据架构中的请求和响应类型生成类.然后,我将这些类用作参数并在 JAX-WS 带注释的端点类上返回类型.
Next I use the JAXB command xjc to generate classes based on the request and response types in my schema. I then use this classes as parameters and return types on a JAX-WS annotated endpoint class.
这给了我一个可以调用的网络服务.它让我可以更好地控制正在发送和返回的 xml,而且还可以自动执行编写完整 wsdl 所需的重复操作.
This now gives me a web service I can call. It gives me more control over the xml being sent and returned but also automates the repetition required in writing the full wsdl.
问题
在架构中,我有一个这样的元素:
Problem
In the schema I have an element like this:
<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" />
所以我想区分用户设置为空还是空白.生成的类则具有此属性.
So I want to distinguish between the user setting null or blank. The generated class then has this attribute.
@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class)
protected JAXBElement<String> myElement;
这样做的效果是该元素既不是可空的也不是可选的.JAX-WS 作为 wsdl 的一部分编写的模式已将该元素设置为强制且不可为空,如果我关闭模式验证,我仍然无法将 nil 传递给我的对象.
The effect of this is that the element becomes neither nillable or optional. The schema that JAX-WS writes as part of the wsdl has set the element to mandatory and not nillable and if I turn off schema validation I still can't pass nil through to my object.
尝试过的事情
如果我将其更改为必需和可空的,那么我会得到这个生成的代码.
Things tried
If I change it to be required and nillable then I get this generated code.
@XmlElement(required = true, nillable = true)
protected String myElement;
如果我将其更改为可选且不可为空,那么我会得到这个生成的代码.
If I change it to optional and not nillable then I get this generated code.
protected String myElement
因此,如果您使用 JAXB,您似乎可以同时拥有其中的一个或两者.彻底失望了!
So you can have either or but not both it seems if you use JAXB. Thoroughly disappointing!
我也尝试过手动将生成的类更改为如下所示.
I've also tried manually changing the generated class to look like this.
@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class, required=false)
protected JAXBElement<String> myElement;
现在这个元素是可选的,但我仍然不能将它设置为 nil.这样做会产生一个值为空字符串的 JAXBElement.仅当您关闭模式验证时才会这样做,因为生成的 JAX-WS wsdl/schema 不会将元素设置为 nillable,因此它不是一个有效的请求.
This now makes the element optional but I still can't set it to nil. Doing so results in a JAXBElement with a value of a blank string. That's only if you turn schema validation off as the resulting JAX-WS wsdl/schema doesn't set the element as nillable, so its not a valid request.
总结
我相信这是 JAXB 的一个错误.@XmlElementRef 注释具有将其设置为不需要的属性,但没有将字段设置为可为空的属性.
Summary
It's my belief that this is a bug with JAXB. The @XmlElementRef annotation has an attribute to set it as not required but there is no attribute to set the field as nullable.
@XmlElement 注释具有 required 和 nullable 属性,但这些属性只会导致 null 对象,因此无法区分未包含在 xml 中的元素或包含但为 null 的元素.这就是为什么您需要将 @XmlElementRef 与 JAXBElement 一起使用.
The @XmlElement annotation has attributes for both required and nullable but these just result in a null object so there would be no way to distinguish between an element not included in the xml or an element that was included but null. This is why you need to use @XmlElementRef along with JAXBElement.
我认为这个错误包括两个问题.首先,xjc 命令应该生成 required=false 的元素.其次,@XmlElementRef 上应该有一个属性来设置元素是否可以为空,这也应该设置.
I think the bug includes two issues. First the xjc command should generate the element with required=false. Second there should be an attribute on @XmlElementRef to set whether the element is nullable and this should be set too.
有人知道修复/解决方法吗?我试着用谷歌搜索,但只发现有人问同样的问题而没有答案.这通常意味着不可能……TIA.
Does anyone know of a fix/workaround? I tried googling but only found people asking the same question without an answer. This usually means it's not possible... TIA.
附加
我使用的是 jaxb 2.2.6,maven 插件是 jaxb2-maven-plugin 1.5.
Additional
I'm using jaxb 2.2.6 and the maven plugin is jaxb2-maven-plugin 1.5.
推荐答案
TL;DR
对于
@XmlElementRef(name="foo", required=false)
protected JAXBElement<String> foo;
文档中不存在的节点将对应于该字段为空.文档中存在 xsi:nil="true"
的 XML 元素将对应于作为 JAXBElement
实例的值,其值为 null
.
An absent node in the document will correspond to this field being null. An XML element present in the document with xsi:nil="true"
will correspond to the value being an instance of JAXBElement
with a value of null
.
您还可以提供一个 XML 模式,而不是让 JAXB 使用包级别 @XmlSchema
注释上的 location
属性生成一个.
You can also provide an XML schema instead of having JAXB generate one using the location
property on the package level @XmlSchema
annotation.
@XmlSchema(
...
location="http://www.example.com/schema/root.xsd")
package forum19665550;
import javax.xml.bind.annotation.XmlSchema;
<小时>
元帅/解组
Java 模型
根
这是一个具有两个字段的对象,可以表示可选数据和可空数据.
This is an object with two fields that can represent optional and nillable data.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
@XmlElementRef(name="foo", required=false)
protected JAXBElement<String> foo;
@XmlElementRef(name="bar", required=false)
protected JAXBElement<String> bar;
}
对象工厂
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
@XmlRegistry
public class ObjectFactory {
@XmlElementDecl(name="foo")
public JAXBElement<String> createFoo(String foo) {
return new JAXBElement<String>(new QName("foo"), String.class, foo);
}
@XmlElementDecl(name="bar")
public JAXBElement<String> createBar(String bar) {
return new JAXBElement<String>(new QName("bar"), String.class, bar);
}
}
演示代码
演示
下面的演示代码将研究 foo
和 bar
值的差异.您可以使用 JAXBIntrospector
类来获取 JAXBElement
实例的实际值.EclipseLink JAXB (MOXy) 中存在一个与解组包装空值的 JAXBElement
实例相关的错误(请参阅:http://bugs.eclipse.org/420746).
The demo code below will investigate the differences in the values for foo
and bar
. You can use the JAXBIntrospector
class to get the real value for an instance of JAXBElement
. There is a bug in EclipseLink JAXB (MOXy) related to unmarshalling an instance of JAXBElement
wrapping a null value (see: http://bugs.eclipse.org/420746).
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum19665550/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
System.out.println("foo was set: " + (root.foo != null));
System.out.println("bar was set: " + (root.bar != null));
System.out.println("foo value: " + root.foo);
System.out.println("bar value: " + root.bar);
System.out.println("foo unwrapped value: " + JAXBIntrospector.getValue(root.foo));
System.out.println("bar unwrapped value: " + JAXBIntrospector.getValue(root.bar));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
在结果输出中,我们看到我们可以区分文档中不存在的元素和具有 `xsi:nil="true" 的元素,并且结果值仍然为 null.
In the resulting output we see that we can differentiate between an element being absent from the document and an element with `xsi:nil="true" and still have the resulting value be null.
foo was set: false
bar was set: true
foo value: null
bar value: javax.xml.bind.JAXBElement@4af42ea0
foo unwrapped value: null
bar unwrapped value: null
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
生成 XML 架构
演示代码
生成架构
下面是一些 JAXB 代码,它们将从带注释的模型生成 XML Schema.
Below is some JAXB code that will generate an XML Schema from the annotated model.
import java.io.IOException;
import javax.xml.bind.*;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
public class GenerateSchema {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
jc.generateSchema(new SchemaOutputResolver() {
@Override
public Result createOutput(String namespaceUri,
String suggestedFileName) throws IOException {
StreamResult result = new StreamResult(System.out);
result.setSystemId(suggestedFileName);
return result;
}
});
}
}
输出
这是生成的 XML 模式.你是对的,它并不表示 foo
和 bar
元素是可空的.
Here is the resulting XML Schema. You are correct that it doesn't indicate that the foo
and bar
elements are nillable.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="bar" type="xs:string"/>
<xs:element name="foo" type="xs:string"/>
<xs:element name="root" type="root"/>
<xs:complexType name="root">
<xs:sequence>
<xs:element ref="foo" minOccurs="0"/>
<xs:element ref="bar" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
<小时>
提供 XML 架构
您可以指向现有的包含更多信息的模型,而不是让 JAXB 从您的模型派生 XML Schema.
Providing an XML Schema
Instead of having JAXB derive an XML Schema from your model, you can point to your existing one that will contain more information.
包裹信息
这是通过在包级别 @XmlSchema
注释上指定 location
属性来完成的.
This is done by specifying the location
property on the package level @XmlSchema
annotation.
@XmlSchema(
...
location="http://www.example.com/schema/root.xsd")
package forum19665550;
import javax.xml.bind.annotation.XmlSchema;
相关文章