JAXB元素既可选又可以为nillable

我重新格式化了这个问题,希望能让我的意图更加清晰。

建筑
我正在编写一些我将使用JAX-WS自己发布的Web服务。 我们已经使用了一段时间的过程是首先编写一个仅定义请求和响应对象的模式。 这将被发送给客户以批准xml消息的结构。 我不想自己编写整个wsdl,因为它比基本模式更复杂。

接下来,我使用JAXB命令xjc根据模式中的请求和响应类型生成类。 然后,我将此类用作参数,并在JAX-WS带注释的端点类上返回类型。

现在这给了我一个我可以打电话的网络服务。 它使我能够更好地控制发送和返回的xml,但也可以自动完成写入完整wsdl所需的重复。

问题
在模式中,我有一个这样的元素:

 

所以我想区分用户设置null或空白。 然后生成的类具有此属性。

 @XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class) protected JAXBElement myElement; 

这样做的结果是元素既不​​是可填充的也不是可选的。 JAX-WS作为wsdl的一部分写入的模式已将元素设置为必需而不是nillable,如果我关闭模式validation,我仍然无法将nil传递给我的对象。

事情尝试了
如果我将其更改为必需和​​可空,那么我将获得此生成的代码。

 @XmlElement(required = true, nillable = true) protected String myElement; 

如果我将其更改为可选而不是nillable,那么我将获得此生成的代码。

 protected String myElement 

因此,如果您使用JAXB,您可以使用或不是两者。 完全令人失望!

我也尝试手动将生成的类更改为这样。

 @XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class, required=false) protected JAXBElement myElement; 

这现在使元素可选,但我仍然无法将其设置为nil。 这样做会导致JAXBElement的值为空字符串。 只有当你关闭模式validation时才会这样做,因为生成的JAX-WS wsdl / schema没有将元素设置为nillable,因此它不是有效的请求。

概要
我认为这是JAXB的一个错误。 @XmlElementRef注释具有将其设置为不需要的属性,但没有将该字段设置为可空的属性。

@XmlElement注释具有必需和可空的属性,但这些只会导致null对象,因此无法区分xml中未包含的元素或包含但为null的元素。 这就是您需要将@XmlElementRef与JAXBElement一起使用的原因。

我认为这个bug包含两个问题。 首先,xjc命令应生成required = false的元素。 其次,@ XmlElementRef应该有一个属性来设置元素是否可以为空,这也应该设置。

有谁知道修复/解决方法? 我尝试使用谷歌搜索,但只发现人们在没有答案的情况下问同样的问题。 这通常意味着它不可能…… TIA。

额外
我正在使用jaxb 2.2.6,而maven插件是jaxb2-maven-plugin 1.5。

TL; DR

对于

 @XmlElementRef(name="foo", required=false) protected JAXBElement foo; 

文档中缺少的节点将对应于该字段为空。 使用xsi:nil="true"的文档中存在的XML元素将对应于值为nullJAXBElement实例的值。

您还可以提供XML模式,而不是让JAXB使用包级别@XmlSchema批注上的location属性生成一个模式。

 @XmlSchema( ... location="http://www.example.com/schema/root.xsd") package forum19665550; import javax.xml.bind.annotation.XmlSchema; 

元帅/解组

Java模型

这是一个具有两个字段的对象,可以表示可选和可存档的数据。

 import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Root { @XmlElementRef(name="foo", required=false) protected JAXBElement foo; @XmlElementRef(name="bar", required=false) protected JAXBElement bar; } 

的ObjectFactory

 import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; import javax.xml.namespace.QName; @XmlRegistry public class ObjectFactory { @XmlElementDecl(name="foo") public JAXBElement createFoo(String foo) { return new JAXBElement(new QName("foo"), String.class, foo); } @XmlElementDecl(name="bar") public JAXBElement createBar(String bar) { return new JAXBElement(new QName("bar"), String.class, bar); } } 

演示代码

演示

下面的演示代码将调查foobar的值的差异。 您可以使用JAXBIntrospector类来获取JAXBElement实例的实际值。 EclipseLink JAXB(MOXy)中存在一个与解组包装空值的JAXBElement实例相关的错误(请参阅: 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中/输出

在结果输出中,我们看到我们可以区分文档中缺少的元素和“xsi:nil =”true“的元素,并且结果值仍为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模式

演示代码

GenerateSchema

下面是一些JAXB代码,它将从带注释的模型生成XML Schema。

 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 Schema。 你是对的,它并不表示foobar元素是可以为空的。

             

提供XML模式

您可以指向包含更多信息的现有XML Schema,而不是让JAXB从您的模型中派生XML Schema。

包信息

这是通过在包级别@XmlSchema注释上指定location属性来完成的。

 @XmlSchema( ... location="http://www.example.com/schema/root.xsd") package forum19665550; import javax.xml.bind.annotation.XmlSchema; 

据我了解你Ben以下的XSD:

  

应该导致:

 @XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class, required = false) protected JAXBElement myElement; 

对?

但是对于默认的JAXB实现,情况并非如此。 看起来像JAXB中的错误。 我没有在JAXB 问题跟踪器中找到它。 在2009年左右,JAXB 2.2中的@XmlElementRef引入了required属性,但显然没有人为此问题创建问题。

使用“绑定自定义”无法更改required属性。

在这种情况下,您可以:

  • 为XJC编写自己的插件,为@XmlElementRef注释添加缺少的属性。 这并不困难。 更多信息在这里 。
  • 使用替代JAXB实现(MOXy工作正常 – 使用MOXy JAXB编译器生成required = false
  • 等待Oracle JAXB的实现得到修复。

无论您选择哪个选项,请在JAXB问题跟踪器中提出问题,以便解决问题。

编辑:

为了表明创建插件很容易,我创建了一个。 你可以在我的github存储库中找到它。 随意使用/复制/修改。 我不保证它100%有效,但简单的情况就像魅力一样。

EDIT2:

如果基于java对象和JAXB注释生成的模式与您的接口不匹配,那么您可以使用@WebService.wsdlLocation指向原始的,正确的WSDL和XSD文件。

EDIT3:

在您的情况下, nil会忽略nil这很奇怪。 我使用JAXB 2.2.6和2.2.7运行测试,并且正确识别nil

 JAXBContext context = JAXBContext.newInstance(SomeElement.class); Unmarshaller unmarshaller = context.createUnmarshaller(); String xml = ""; SomeElement someElement = (SomeElement) unmarshaller .unmarshal(new StringReader(xml)); assertThat(someElement.getMyElement().isNil(), is(true)); 

你能检查一下你是否正确设置了nil属性,例如:

  

如果是正确的,请尝试与您的class级一起进行测试。

您可以自定义绑定

  

正如Customized Binding中所述 ,对于您要问的相同的情况。

我正在使用自定义绑定maven插件org.jvnet.jaxb2.maven2:maven-jaxb2-plugin