2017-10-09 2 views
0

J'ai besoin d'analyser du contenu XML pour lequel j'ai le XSD. En général, c'est simple. Cependant, dans un cas particulier, le XML inclut parfois l'espace de noms XML et parfois non. En outre, il n'est pas vraiment pratique d'exiger l'espace de noms XML, car le XML fourni provient de plusieurs sources. Donc, je suis coincé avec essayer de trouver un moyen de contourner cela. Comme indiqué, j'ai le XSD pour le XML et j'ai utilisé XJC (à partir de JAXB) pour générer les classes d'entités XML correspondantes à partir de la XSD.Comment analyser du contenu XML avec ou sans espace de nom?

XML exemple y compris l'espace de noms:

<?xml version="1.0" encoding="UTF-8"?> 
<root xmlns="http://www.w3.org/namespace/"> 
    <foo id="123> 
     <bar>value</bar> 
    </foo> 
</root> 

XML exemple l'exclusion de l'espace de noms:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <foo id="123> 
     <bar>value</bar> 
    </foo> 
</root> 

Comme vous pouvez le voir, le contenu XML est identique dans la structure - la seule différence est l'attribut xmlxs sur l'entité root.

Mon code est le suivant:

URI uri = <URI of XML file> 
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
factory.setNamespaceAware(true); 
Node node = builder.parse(uri.toString()); // Parsing succeeds, ie. the XML is valid. 
JAXBContext context = JAXBContext.newInstance("com.example.xml"); 
Unmarshaller parser = context.createUnmarshaller(); 
// Next line succeeds or fails, depending on presence of namespace 
Object object = parser.unmarshal(node); 

Le XML est toujours correctement analysé dans un Node. Si l'attribut xmlns est présent dans le fichier XML, l'ensemble du processus se termine normalement et je reçois une instance d'une classe com.example.xml.Root (générée à l'aide de XJC). De là, je peux accéder aux objets Foo et Bar.

Si l'attribut xmlns est absent, le unmarshalling échoue à l'exception suivante:

javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"root"). 
    Expected elements are <{http://www.w3.org/namespace/}root>, 
    <{http://www.w3.org/namespace/}foo>, 
    <{http://www.w3.org/namespace/}bar> 

J'ai essayé unmarmshalling by declared type avec un succès limité. Plus précisément, le unmarshalling terminé sans erreur. Cependant, la classe Root résultante ne contient aucun objet Foo ou Bar.

Le code pour cela implique de changer la dernière ligne:

Object object = parser.unmarshal(node, Root.class); 

J'ai essayé unmarshalling avec le drapeau « espace de nom conscient » mis à false, mais cela a échoué avec une erreur.

J'ai pensé à ajouter un espace de noms au node s'il n'en a pas, avant de le désassembler. Cependant, l'API ne semble pas permettre cela.

Une autre idée que j'avais était d'avoir deux ensembles de classes générées, une pour chaque cas (espace de noms, pas d'espace de noms). Cependant, cela semble trop kludge.

Alors je suis coincé? Aucune suggestion? Ou est ce que j'essaye de faire impossible?

Répondre

1

Vous pouvez faire avec un filtre XML. Voici mon exemple pour vous, pour supprimer le ns où il est présent.

package testjaxb; 

import java.io.StringReader; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.transform.sax.SAXSource; 
import org.xml.sax.Attributes; 
import org.xml.sax.InputSource; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 
import org.xml.sax.helpers.XMLFilterImpl; 
import org.xml.sax.helpers.XMLReaderFactory; 

public class MarshalWithFilter { 

    public static void main(String[] args) throws Exception { 
     String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 
       + "<root xmlns=\"http://www.w3.org/namespace/\">\n" 
       + " <foo id=\"123\">\n" 
       + "  <bar>value</bar>\n" 
       + " </foo>\n" 
       + "</root>"; 

     String xmlStringWithoutNs = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 
       + "<root>\n" 
       + " <foo id=\"123\">\n" 
       + "  <bar>value</bar>\n" 
       + " </foo>\n" 
       + "</root>"; 

     Root r = (Root) unmarshal(xmlString); 
     System.out.println("root.." + r.getFoo().getId()); 
     System.out.println("root.." + r.getFoo().getBar()); 
     r = (Root) unmarshal(xmlStringWithoutNs); 
     System.out.println("root.." + r.getFoo().getId()); 
     System.out.println("root.." + r.getFoo().getBar()); 
    } 

    private static Root unmarshal(String sampleXML) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(Root.class); 
     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     XMLReader reader = XMLReaderFactory.createXMLReader(); 
     IngoreNamespaceFilter nsFilter = new IngoreNamespaceFilter(); 
     nsFilter.setParent(reader); 
     StringReader stringReader = new StringReader(sampleXML); 
     InputSource is = new InputSource(stringReader); 
     SAXSource source = new SAXSource(nsFilter, is); 
     System.out.println("" + sampleXML); 
     return (Root) unmarshaller.unmarshal(source); 
    } 
} 

class IngoreNamespaceFilter extends XMLFilterImpl { 

    public IngoreNamespaceFilter() { 
     super(); 
    } 

    @Override 
    public void startDocument() throws SAXException { 
     super.startDocument(); 
    } 

    @Override 
    public void startElement(String arg0, String arg1, String arg2, 
      Attributes arg3) throws SAXException { 

     super.startElement("", arg1, arg2, arg3); //Null uri 
    } 

    @Override 
    public void endElement(String arg0, String arg1, String arg2) 
      throws SAXException { 

     super.endElement("", arg1, arg2); //null url 
    } 

    @Override 
    public void startPrefixMapping(String prefix, String url) 
      throws SAXException { 
     //ignore namessopace 

    } 

} 

Et ci-dessous sont POJO:

racine

package testjaxb; 

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlRootElement; 

@XmlRootElement(name="root") 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Root 
{ 
    private Foo foo; 


    public Foo getFoo() 
    { 
     return foo; 
    } 

    public void setFoo (Foo foo) 
    { 
     this.foo = foo; 
    } 


} 

Foo

package testjaxb; 

import javax.xml.bind.annotation.XmlAccessType; 
import javax.xml.bind.annotation.XmlAccessorType; 
import javax.xml.bind.annotation.XmlAttribute; 


@XmlAccessorType(XmlAccessType.FIELD) 
public class Foo 
{ 
    @XmlAttribute 
    private String id; 

    private String bar; 

    public String getId() 
    { 
     return id; 
    } 

    public void setId (String id) 
    { 
     this.id = id; 
    } 

    public String getBar() 
    { 
     return bar; 
    } 

    public void setBar (String bar) 
    { 
     this.bar = bar; 
    } 


} 
+0

T Hanks pour prendre le temps de mettre en place votre solution. Je l'ai reproduit dans mon environnement du mieux que je peux mais ça ne marche pas pour moi. Mon filtre d'espace de nom est utilisé, mais quand 'super.startElement (" ", ...);' est appelé, j'obtiens le même 'UnmarshalException' pour' Root' enregistré dans ma question. Je l'ai débogué pendant un moment sans aucune chance. – dave

+0

@dave J'ai testé ceci avec votre exemple de xml collé en question. Avez-vous différents xml qui échoue. – Optional

+0

Votre solution a fonctionné, donc je dois faire quelque chose de stupide. J'ai dormi dessus et j'ai examiné les différences. J'ai commencé avec un 'URI', pas un' String', mais je pouvais voir que j'avais travaillé autour de ça. Cela laissait les POJO annotés. Les vôtres ont été codés à la main et les miennes sont générées. Et puis ça m'a frappé, j'utilisais les classes générées à partir du XSD _contenant l'espace de noms_ à la place de celui sans. Après tout, nous supprimons l'espace de noms du XML, donc les classes ne devraient pas l'avoir non plus. Après je suis passé à l'ensemble correct de classes générées tout a bien fonctionné. Merci encore. – dave