質問を再フォーマットして、意図がより明確になることを願っています。
アーキテクチャ
私は、JAX-WSを使用して自分で公開するWebサービスをいくつか作成しています。しばらくの間使用してきたプロセスは、最初に要求オブジェクトと応答オブジェクトのみを定義するスキーマを記述することです。これは、xmlメッセージの構造を承認するために顧客に送信されます。基本的なスキーマよりも複雑なので、私はwsdl全体を自分で書きたくありません。
次に、JAXBコマンドxjcを使用して、スキーマの要求および応答タイプに基づいてクラスを生成します。次に、このクラスをパラメーターとして使用し、JAX-WS注釈付きエンドポイントクラスの型を返します。
これにより、呼び出し可能なWebサービスが提供されます。これにより、送信および返されるXMLをより詳細に制御できますが、完全なwsdlの書き込みに必要な繰り返しも自動化されます。
問題
スキーマには次のような要素があります:
<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" />
したがって、ユーザー設定のnullまたは空白を区別したいと思います。生成されたクラスには、この属性があります。
@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class)
protected JAXBElement<String> myElement;
これの効果は、要素がnillableまたはオプションにならないことです。 JAX-WSがwsdlの一部として書き込むスキーマは、要素を必須でnillableに設定していないため、スキーマ検証をオフにしても、オブジェクトにnilを渡すことができません。
試したこと
それを必須でnillableに変更すると、この生成されたコードを取得します。
@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<String> myElement;
これで要素がオプションになりましたが、まだnilに設定できません。これにより、空の文字列の値を持つJAXBElementが生成されます。結果のJAX-WS wsdl/schemaが要素をnillableに設定しないため、スキーマ検証をオフにした場合にのみ有効であり、有効なリクエストではありません。
概要
これはJAXBのバグであると私は信じています。 @XmlElementRefアノテーションには、必須ではないように設定するための属性がありますが、フィールドをnull可能に設定するための属性はありません。
@XmlElementアノテーションには、必須とnull可能の両方の属性がありますが、これらは単にnullオブジェクトになるため、xmlに含まれていない要素と含まれているがnullの要素を区別する方法はありません。これが、JAXBElementとともに@XmlElementRefを使用する必要がある理由です。
バグには2つの問題が含まれていると思います。最初に、xjcコマンドはrequired = falseの要素を生成する必要があります。次に、要素がnull可能かどうかを設定するための属性が@XmlElementRefにあり、これも設定する必要があります。
誰かが修正/回避策を知っていますか?私はグーグルで試しましたが、答えなしで同じ質問をしている人々だけを見つけました。これは通常、それが不可能であることを意味します... TIA。
追加
私はjaxb 2.2.6を使用していますが、mavenプラグインはjaxb2-maven-plugin 1.5です。
For
@XmlElementRef(name="foo", required=false)
protected JAXBElement<String> foo;
ドキュメントにノードがない場合は、このフィールドがnullであることに対応します。 xsi:nil="true"
を使用してドキュメントに存在するXML要素は、JAXBElement
の値を持つnull
のインスタンスである値に対応します。
パッケージレベルの@XmlSchema
アノテーションでlocation
プロパティを使用してJAXBにスキーマを生成させる代わりに、XMLスキーマを提供することもできます。
@XmlSchema(
...
location="http://www.example.com/schema/root.xsd")
package forum19665550;
import javax.xml.bind.annotation.XmlSchema;
ルート
これは、オプションでnillableなデータを表すことができる2つのフィールドを持つオブジェクトです。
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;
}
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<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
のインスタンスを非整列化してnull値をラップすることに関連するバグがあります( 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のままであることがわかります。
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"/>
GenerateSchema
以下は、注釈付きモデルからXMLスキーマを生成するいくつかのJAXBコードです。
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
要素が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>
JAXBにモデルからXMLスキーマを派生させる代わりに、より多くの情報を含む既存のスキーマを指すことができます。
package-info
これは、パッケージレベルの@XmlSchema
アノテーションでlocation
プロパティを指定することによって行われます。
@XmlSchema(
...
location="http://www.example.com/schema/root.xsd")
package forum19665550;
import javax.xml.bind.annotation.XmlSchema;
私が理解しているように、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 issue tracker では見つかりませんでした。 required
属性は、2009年頃にJAXB 2.2の@XmlElementRef
に導入されましたが、明らかにこの問題の問題を作成した人はいません。
required
属性は、バインディングのカスタマイズを使用して変更できません。
この状況では、次のことができます。
@XmlElementRef
アノテーションに追加します。それはそれほど難しいことではありません。詳細情報 こちら 。required = false
は MOXy JAXBコンパイラを使用して生成されます )どちらのオプションを選択する場合でも、問題が修正されるように、 JAXB Issue Tracker で問題を報告してください。
編集:
プラグインの作成が簡単であることを示すために、プラグインを作成しました。私の githubリポジトリ にあります。自由に使用/コピー/変更してください。 100%動作することを保証するものではありませんが、単純なケースでは魅力のように動作します。
EDIT2:
JavaオブジェクトおよびJAXBアノテーションに基づいて生成されたスキーマがインターフェースと一致しない場合は、@WebService.wsdlLocation
を使用して、元の正しいWSDLおよびXSDファイルを指すことができます。
EDIT3:
あなたの場合、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"/>
正しい場合は、クラスでテストを実行してみてください。
次の方法でバインディングをカスタマイズできます
<jaxb:globalBindings generateElementProperty="false" />
Customized Binding に記載されているように、あなたが求めているのとまったく同じケースについて。
Mavenプラグインでカスタムバインディングを使用しています org.jvnet.jaxb2.maven2:maven-jaxb2-plugin