Java JAXB元素,它既是可选的,也是可为零的

Java JAXB元素,它既是可选的,也是可为零的,java,xml,jaxb,jax-ws,Java,Xml,Jaxb,Jax Ws,我已经重新格式化了这个问题,希望能让我的意图更清楚 架构 我正在编写一些web服务,我将使用JAX-WS发布这些服务。我们已经使用了一段时间的过程是首先编写一个只定义请求和响应对象的模式。这将发送给客户以批准xml消息的结构。我不想自己编写整个wsdl,因为它比基本模式更复杂 接下来,我使用JAXB命令xjc根据模式中的请求和响应类型生成类。然后,我将这些类用作JAX-WS注释端点类的参数和返回类型 这现在给了我一个可以调用的web服务。它使我能够更好地控制发送和返回的xml,但也自动化了编写完

我已经重新格式化了这个问题,希望能让我的意图更清楚

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

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

这现在给了我一个可以调用的web服务。它使我能够更好地控制发送和返回的xml,但也自动化了编写完整wsdl所需的重复

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

<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" /> 
如果我将其更改为可选且不为nillable,那么我将得到生成的代码

@XmlElement(required = true, nillable = true)
protected String myElement;
protected String myElement
因此,如果使用JAXB,您可以选择其中之一,但不能同时选择两者。完全令人失望

我还尝试手动更改生成的类,使其看起来像这样

@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class, required=false)
protected JAXBElement<String> myElement;
@xmlementref(name=“myElement”,namespace=“/mynamespace”,type=JAXBElement.class,required=false)
保护性脊髓损伤;
这使得元素成为可选的,但我仍然无法将其设置为nil。这样做会产生一个值为空字符串的JAXBElement。只有当您关闭模式验证时,才可以这样做,因为生成的JAX-WSWSDL/schema没有将元素设置为nillable,因此它不是有效的请求

摘要
我相信这是JAXB的一个bug。@XmlElementRef注释有一个将其设置为非必需的属性,但没有将字段设置为可空的属性

@XmlElement注释具有required和nullable属性,但这些属性只会导致一个null对象,因此无法区分xml中未包含的元素和包含但为null的元素。这就是为什么需要将@xmlementref与JAXBElement一起使用

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

有人知道修复/解决方法吗?我试着用谷歌搜索,但只发现有人问同样的问题却没有答案。这通常意味着这是不可能的。。。蒂亚

附加
我使用的是jaxb2.2.6,maven插件是jaxb2 maven插件1.5。

TL;博士 为了


封送/解封 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<String> foo;

    @XmlElementRef(name="bar", required=false)
    protected JAXBElement<String> bar;

}
input.xml/Output

在结果输出中,我们可以区分文档中缺少的元素和带有`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 version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
输出

下面是生成的XML模式。您是正确的,它没有指示
foo
bar
元素为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>

据我所知,您需要以下XSD:

<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" /> 
能否检查您是否正确设置了nil属性,例如:

<myElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>


如果正确,请尝试使用您的类运行测试。

您可以通过

<jaxb:globalBindings generateElementProperty="false" />

如中所述,对于您询问的同一个确切案例


我正在使用maven插件的自定义绑定

我想你误解了。首先,这是一个请求,因此它将xml封送到一个对象中,而不是反过来。第二,当我将这个生成的类作为web服务的一部分使用时,wsdl将这个元素显示为必需的且不可为零。我不明白为什么在输出上会有任何不同。可能会将JAXBElement设置为null,但响应不会根据wsdl/schema进行验证。无论如何,谢谢你,我已经重新编写了一些内容,希望能让它更清楚。@BenThurley-我已经更新了我的答案,希望能更好地匹配你的问题。我不能轻易地将它指向一个schema。我使用JAX-WS将其部署为web服务,因此JAX-WS使用JAXB注释创建wsdl。我可以手工编写wsdl并指向它,但我不想这样做,我已经尝试手动添加required=false属性。这使得它是可选的,但仍然不能为零。即使我关闭了模式验证,它仍然没有将JAXBElement设置为nill。i、 e.getNil返回false。我可能必须按照您的建议提出错误。对不起,我不知道是什么将JAXBElement设置为nill。您的意思是输入消息未正确解组吗?顺便说一句,我更新了我的答案,以便您可以解决生成的XML模式不正确的问题。是的,如果您将传入元素的web服务称为nil,那么它不会反映在生成的JAXBElement中。如果在JAXBElement上调用getValue,则会得到一个空字符串。如果你叫isNil,你会得到false。它也无法对模式进行验证,因此我必须关闭它才能达到这一目的。设置wsdl位置实际上不是一个选项,因为我只有一个模式来定义请求和响应类型。我希望JAX-WS为我生成其余的wsdl。据我所知@xmlementref不支持可选和nillable。它只有一个需要设置的属性。我无法错误地再现JAXB解组
nil
的问题。请看一下我的最新编辑。我使用您的示例生成一些xml,然后再转换回一个对象。我不得不摆弄它一点,但最终我能够让它编组和解编。然而,当涉及到模式验证时,我仍然没有把握,因为元素在sche中不可为零
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"/>
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 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>
@XmlSchema(
    ...
    location="http://www.example.com/schema/root.xsd")
package forum19665550;

import javax.xml.bind.annotation.XmlSchema;
<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" /> 
@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class, required = false)
protected JAXBElement<String> myElement;
JAXBContext context = JAXBContext.newInstance(SomeElement.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><ns2:someElement xmlns:ns2=\"http://www.example.org/example/\"><myElement xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/></ns2:someElement>";
SomeElement someElement = (SomeElement) unmarshaller
        .unmarshal(new StringReader(xml));
assertThat(someElement.getMyElement().isNil(), is(true));
<myElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<jaxb:globalBindings generateElementProperty="false" />