web-dev-qa-db-fra.com

Jaxb: comment décompresser xs: n'importe quelle partie de chaîne XML?

J'ai une application effectuant des conversions XML <-> à l'aide de Jaxb et des classes générées automatiquement avec maven-jaxb2-plugin. 

Quelque part au fond de mon schéma, j'ai la possibilité d'entrer "ANY" xml. 

Mise à jour: cela décrit mieux mon schéma. Certains XML connus enveloppant une partie totalement inconnue (la partie "toute").

<xs:complexType name="MessageType">
  <xs:sequence>
    <xs:element name="XmlAnyPayload" minOccurs="0">
        <xs:complexType>
            <xs:sequence>
                <xs:any namespace="##any"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="OtherElements">
        ....
</xs:sequence>

Ceci mappe (par jaxb) vers une classe interne comme celle-ci.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "any"
})
public static class XmlAnyPayload {

    @XmlAnyElement(lax = true)
    protected Object any;

Quand je démonte la structure entière, ce n'est pas un problème. Le "Object any" sera rendu dans un org.Apache.xerces.dom.ElementNSImpl. Maintenant, je veux recréer l’objet Java manuellement, puis passer au XML. Comment prendre un XML aléatoire et le placer dans l'élément any (org.Apache.xerces.dom.ElementNSImpl) pour pouvoir créer l'objet Java?

De plus, le cas suivant est lorsque j’ai cet élément en tant que Java, je veux annoncer cette partie (pour pouvoir extraire la chaîne XML de cet élément). Mais ce n'est pas possible. Je reçois une exception concernant les éléments racines. Mais il n'est pas possible d'annoter ElementNSImpl.

unable to marshal type "com.Sun.org.Apache.xerces.internal.dom.ElementNSImpl" as an element because it is missing an @XmlRootElement annotation

Avez-vous des suggestions sur la façon de gérer ces problèmes?

21
Petter Nordlander

@XmlAnyElement(lax = true) signifie en anglais simple quelque chose comme:

Cher JAXB! Si vous avez un mappage pour cet élément, veuillez le décompresser dans un objet Java. Si vous ne connaissez pas cet élément, laissez-le comme Élément DOM.

C'est exactement ce qui se passe dans votre cas. Donc, si vous voulez réellement disséminer le contenu de ce laxique, fournissez au contexte JAXB un mappage pour l'élément que vous souhaitez dissocier. Pour ce faire, le plus simple consiste à annoter votre classe avec @XmlRootElement

@XmlRootElement(name="foo", namespace="urn:bar")
public class MyClass { ... }

Maintenant, lorsque vous créez votre contexte JAXB, ajoutez-y MyClass:

JAXBContext context = JAXBContext.newInstance(A.class, B.class, ..., MyClass.class);

Dans ce cas, si JAXB rencontre l’élément {urn:bar}foo à la place de ce xs:any, il saura que cet élément est mappé sur MyClass et tentera d’annuler la fusion de MyClass.

Si vous créez un contexte JAXB basé sur le nom du package (vous le faites probablement), vous pouvez toujours y ajouter votre classe (par exemple, com.acme.foo.MyClass). Le moyen le plus simple est de créer une ressource com/acme/foo/jaxb.index:

com.acme.foo.MyClass

Et ajoutez le nom de votre paquet au chemin de contexte:

JAXBContext context = JAXBContext.newInstance("org.dar.gee.schema:com.acme.foo");

ObjectFactory etc. existe d'autres manières, mais le truc avec jaxb.index est probablement le plus simple.

Alternativement, au lieu de tout disséminer en une seule exécution, vous pouvez laisser le contenu de xs:any sous le nom DOM et le disséminer dans l'objet cible dans une seconde dissémination avec un autre contexte JAXB (qui connaît votre classe MyClass Quelque chose comme:

JAXBContext payloadContext = JAXBContext.newInstance(MyClass.class);
payloadContext.createUnmarshaller().unmarshal((Node) myPayload.getAny());

Cette approche est parfois meilleure, en particulier lorsque vous combinez des schémas conteneur/charge utile relativement indépendants. Cela dépend du cas.

Tout ce qui précède s’applique également au marshalling. Tout est parfaitement bidirectionnel.

39
lexicore

Je pense que vous avez besoin des XSD pour cette partie "any" et que vous générez des classes pour eux aussi.

Voici quelques informations supplémentaires:

http://jaxb.Java.net/guide/Mapping_of__xs_any___.html

Éditer: si votre objet que vous voulez marshal n'a pas l'annotation @XmlRootElement (voir le message d'erreur), alors je pense que vous devez l'envelopper avec un JAXBElement.

1
Puce
<xs:any/>

nécessite que certains éléments non intuitifs soient convertis en objet Java. Si vous n’avez aucune différence, essayez d’utiliser 

<element name="any" type="xs:anyType"/>
0
korifey