J'ai reformaté la question pour, je l'espère, clarifier mes intentions.
Architecture
J'écris certains services Web que je publierai moi-même à l'aide de JAX-WS. Le processus que nous utilisons depuis un certain temps consiste à écrire d'abord un schéma qui définit uniquement les objets de demande et de réponse. Celui-ci est envoyé au client pour approuver la structure des messages xml. Je ne veux pas écrire le WSDL moi-même car c'est plus compliqué que le schéma de base.
Ensuite, j'utilise la commande JAXB xjc pour générer des classes en fonction des types de demande et de réponse dans mon schéma. J'utilise ensuite ces classes comme paramètres et renvoie des types sur une classe de point de terminaison annotée JAX-WS.
Cela me donne maintenant un service Web que je peux appeler. Cela me donne plus de contrôle sur le xml envoyé et retourné mais automatise également la répétition requise lors de l'écriture du wsdl complet.
Problème
Dans le schéma, j'ai un élément comme celui-ci:
<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" />
Je veux donc faire la distinction entre le paramètre utilisateur nul ou vide. La classe générée a alors cet attribut.
@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class)
protected JAXBElement<String> myElement;
L'effet de ceci est que l'élément ne devient ni illisible ni facultatif. Le schéma que JAX-WS écrit dans le cadre de wsdl a défini l'élément sur obligatoire et non nillable et si je désactive la validation de schéma, je ne peux toujours pas transmettre nil à mon objet.
Choses essayées
Si je le change pour qu'il soit obligatoire et qu'il soit possible de le supprimer, j'obtiens ce code généré.
@XmlElement(required = true, nillable = true)
protected String myElement;
Si je le change en optionnel et non nillable, j'obtiens ce code généré.
protected String myElement
Donc, vous pouvez avoir l'un ou l'autre mais pas les deux, semble-t-il, si vous utilisez JAXB. Vraiment décevant!
J'ai également essayé de changer manuellement la classe générée pour qu'elle ressemble à ceci.
@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class, required=false)
protected JAXBElement<String> myElement;
Cela rend désormais l'élément facultatif mais je ne peux toujours pas le définir sur zéro. Cela entraîne un JAXBElement avec une valeur de chaîne vide. C'est seulement si vous désactivez la validation du schéma car le schéma/fichier wsdl/JAX-WS résultant ne définit pas l'élément comme pouvant être supprimé, donc ce n'est pas une demande valide.
Résumé
Je pense que c'est un bug avec JAXB. L'annotation @XmlElementRef a un attribut pour le définir comme non requis mais il n'y a aucun attribut pour définir le champ comme nullable.
L'annotation @XmlElement a des attributs à la fois obligatoires et nullables, mais ceux-ci se traduisent simplement par un objet null, il n'y aurait donc aucun moyen de distinguer entre un élément non inclus dans le xml ou un élément qui était inclus mais nul. C'est pourquoi vous devez utiliser @XmlElementRef avec JAXBElement.
Je pense que le bug comprend deux problèmes. La commande xjc doit d'abord générer l'élément avec required = false. Deuxièmement, il devrait y avoir un attribut sur @XmlElementRef pour définir si l'élément est nullable et cela devrait également être défini.
Quelqu'un connaît-il un correctif/une solution de contournement? J'ai essayé de googler mais je n'ai trouvé que des gens posant la même question sans réponse. Cela signifie généralement que ce n'est pas possible ... TIA.
supplémentaire
J'utilise jaxb 2.2.6 et le plugin maven est jaxb2-maven-plugin 1.5.
For
@XmlElementRef(name="foo", required=false)
protected JAXBElement<String> foo;
Un nœud absent dans le document correspondra à la nullité de ce champ. Un élément XML présent dans le document avec xsi:nil="true"
correspondra à la valeur étant une instance de JAXBElement
avec une valeur de null
.
Vous pouvez également fournir un schéma XML au lieu d'en générer un par JAXB à l'aide de la propriété location
au niveau du package @XmlSchema
annotation.
@XmlSchema(
...
location="http://www.example.com/schema/root.xsd")
package forum19665550;
import javax.xml.bind.annotation.XmlSchema;
Racine
Il s'agit d'un objet avec deux champs qui peuvent représenter des données facultatives et pouvant être supprimées.
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);
}
}
Démo
Le code de démonstration ci-dessous étudiera les différences dans les valeurs de foo
et bar
. Vous pouvez utiliser la classe JAXBIntrospector
pour obtenir la valeur réelle d'une instance de JAXBElement
. Il y a un bogue dans EclipseLink JAXB (MOXy) lié au dé-marshalling d'une instance de JAXBElement
encapsulant une valeur nulle (voir: 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
Dans la sortie résultante, nous voyons que nous pouvons faire la différence entre un élément absent du document et un élément avec `xsi: nil =" true "et que la valeur résultante soit toujours nulle.
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
Vous trouverez ci-dessous du code JAXB qui générera un schéma XML à partir du modèle annoté.
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;
}
});
}
}
Sortie
Voici le schéma XML résultant. Vous avez raison, cela n'indique pas que les éléments foo
et bar
sont nillables.
<?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>
Au lieu que JAXB dérive un schéma XML de votre modèle, vous pouvez pointer vers votre schéma existant qui contiendra plus d'informations.
info-paquet
Cela se fait en spécifiant la propriété location
au niveau du package @XmlSchema
annotation.
@XmlSchema(
...
location="http://www.example.com/schema/root.xsd")
package forum19665550;
import javax.xml.bind.annotation.XmlSchema;
Si je vous comprends bien Ben le XSD suivant:
<xs:element name="myElement" type="xs:string" nillable="true" minOccurs="0" />
Devrait se traduire par:
@XmlElementRef(name = "myElement", namespace = "/mynamespace", type = JAXBElement.class, required = false)
protected JAXBElement<String> myElement;
Droite?
Mais pour l'implémentation JAXB par défaut, ce n'est pas le cas. Ressemble à un bogue dans JAXB. Je ne l'ai pas trouvé dans JAXB tracker problème . L'attribut required
a été introduit dans @XmlElementRef
dans JAXB 2.2 vers 2009, mais apparemment personne n'a créé de problème pour ce problème.
L'attribut required
ne peut pas être modifié à l'aide des personnalisations de liaison.
Dans cette situation, vous pouvez:
@XmlElementRef
annotation. Ce n'est pas si difficile. Plus d'informations ici .required = false
est généré à l'aide de compilateur MOXy JAXB )Peu importe l'option que vous choisissez, veuillez soulever le problème dans JAXB issue tracker afin que le problème soit résolu.
MODIFIER:
Pour montrer qu'il est facile de créer un plugin, j'en ai créé un. Vous pouvez le trouver dans mon dépôt github . N'hésitez pas à utiliser/copier/modifier à volonté. Je ne garantis pas que cela fonctionne à 100%, mais pour les cas simples, cela fonctionne comme un charme.
EDIT2:
Si le schéma généré est basé sur Java et les annotations JAXB ne correspondent pas à votre interface, vous pouvez utiliser @WebService.wsdlLocation
pour pointer vers vos fichiers WSDL et XSD corrects d'origine.
EDIT3:
C'est bizarre que nil
soit ignoré par JAXB dans votre cas. J'ai effectué un test en utilisant JAXB 2.2.6 et 2.2.7 et nil
est correctement reconnu:
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));
Pourriez-vous vérifier si vous avez correctement défini l'attribut nil, par exemple:
<myElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
S'il est correct, essayez d'exécuter le test avec votre classe.
Vous pouvez personnaliser la reliure en
<jaxb:globalBindings generateElementProperty="false" />
Comme indiqué dans Liaison personnalisée , pour le même cas exact que vous demandez.
J'utilise une liaison personnalisée avec le plugin maven org.jvnet.jaxb2.maven2: maven-jaxb2-plugin