11

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

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

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

现在,这给了我一个可以调用的 Web 服务。它让我对发送和返回的 xml 有更多的控制,而且还自动化了编写完整 wsdl 所需的重复。

问题
在架构中,我有一个这样的元素:

<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" /> 

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

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

这样做的效果是该元素既不是可空的也不是可选的。JAX-WS 作为 wsdl 的一部分编写的模式已将该元素设置为强制且不可为空,如果我关闭模式验证,我仍然无法将 nil 传递给我的对象。

尝试过的事情
如果我将其更改为必需且可空的,那么我会得到这个生成的代码。

@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;

这现在使元素成为可选的,但我仍然不能将它设置为 nil。这样做会产生一个值为空字符串的 JAXBElement。仅当您关闭模式验证时,因为生成的 JAX-WS wsdl/schema 不会将元素设置为可空,因此它不是有效的请求。

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

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

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

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

另外
我使用的是 jaxb 2.2.6,maven 插件是 jaxb2-maven-plugin 1.5。

4

3 回答 3

13

TL;博士

为了

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

文档中不存在的节点将对应于该字段为空。文档中存在的 XML 元素xsi:nil="true"将对应于作为 的实例的JAXBElement值,其值为null

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

@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<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 )。

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);
    }

}

输入.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 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 模式。

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 模式。你是对的,它并不表示fooandbar元素是可空的。

<?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。

包信息

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

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

import javax.xml.bind.annotation.XmlSchema;
于 2013-10-29T18:23:10.240 回答
3

您可以通过以下方式自定义绑定

<jaxb:globalBindings generateElementProperty="false" />

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

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

于 2015-08-26T10:04:04.730 回答
2

据我了解,您 Ben 以下 XSD:

<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;

正确的?

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

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

在这种情况下,您可以:

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

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

编辑:

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

编辑2:

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

编辑3:

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

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));

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

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

如果正确,请尝试与您的班级一起运行测试。

于 2013-10-30T20:26:09.593 回答