2009-02-24 7 views
3

Je travaille sur un système qui devrait être en mesure de lire n'importe quel fichier XML (ou du moins, bien formé), de manipuler quelques nœuds et de les réécrire dans ce même fichier. Je veux que mon code soit aussi générique que possible et je ne veux pasComment puis-je ignorer la validation DTD mais garder le type Doctype lors de l'écriture d'un fichier XML?

  • références codées en dur aux informations Schema/Doctype n'importe où dans mon code. L'information doctype est dans le document source, je veux garder exactement cette information de doctype et ne pas la fournir de nouveau dans mon code. Si un document n'a pas de DocType, je n'en ajouterai aucun. Je me fiche de la forme ou du contenu de ces fichiers, sauf pour mes quelques nœuds.
  • EntityResolvers personnalisés ou StreamFilters à omettent ou manipuler les informations de source (Il est déjà regrettable que les informations d'espace de noms semble en quelque sorte inaccessible à partir du fichier de document dans lequel il est déclaré, mais je peux gérer à l'aide XPath plus laid)
  • de validation DTD . Je n'ai pas les DTD référencées, je ne veux pas les inclure et la manipulation de Node est parfaitement possible sans les connaître.

Le but est d'avoir le fichier source entièrement inchangé à l'exception des nœuds modifiés qui sont récupérés via XPath. Je voudrais sortir avec les trucs javax.xml standard.

Mon progrès à ce jour:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 

    factory.setAttribute("http://xml.org/sax/features/namespaces", true); 
    factory.setAttribute("http://xml.org/sax/features/validation", false); 
    factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); 
    factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 

    factory.setNamespaceAware(true); 
    factory.setIgnoringElementContentWhitespace(false); 
    factory.setIgnoringComments(false); 
    factory.setValidating(false); 
    DocumentBuilder builder = factory.newDocumentBuilder(); 
    Document document = builder.parse(new InputSource(inStream)); 

Cette charge la source XML dans un org.w3c.dom.Document avec succès, en ignorant la validation DTD. Je peux faire mes remplacements et puis j'utilise

Source source = new DOMSource(document); 
    Result result = new StreamResult(getOutputStream(getPath())); 

    // Write the DOM document to the file 
    Transformer xformer = TransformerFactory.newInstance().newTransformer(); 
    xformer.transform(source, result); 

pour le réécrire. Ce qui est presque parfait. Mais le tag Doctype est parti, peu importe ce que je fais. Pendant le débogage, j'ai vu qu'il y avait un objet DeferredDoctypeImpl [log4j: configuration: null] dans l'objet Document après l'analyse, mais il est en quelque sorte faux, vide ou ignoré. Le fichier que j'ai testé démarre comme ceci (mais il en est de même pour les autres types de fichiers):

<? Xml version = "1.0" encoding = "UTF-8"? >

< DOCTYPE log4j: configuration du système "log4j.dtd" >

< log4j: xmlns de configuration: log4j = "http://jakarta.apache.org/log4j/" debug = "false" >

[...]

Je pense qu'il ya beaucoup de façons (facile?) impliquant hacks ou tirant JARs supplémentaires dans le projet. Mais je préférerais l'avoir avec les outils que j'utilise déjà.

Répondre

2

Désolé, il a obtenu l'aide en ce moment un XMLSerializer au lieu du transformateur ...

+1

Je voudrais voir votre code. Néanmoins, je vais utiliser la grande puissance de Google. @Stephan (l'utilisateur qui n'a pas de compte de log ici) –

0

Voici comment vous pouvez le faire en utilisant le LSSerializer trouvé dans le JDK:

private void writeDocument(Document doc, String filename) 
      throws IOException { 
     Writer writer = null; 
     try { 
      /* 
      * Could extract "ls" to an instance attribute, so it can be reused. 
      */ 
      DOMImplementationLS ls = (DOMImplementationLS) 
        DOMImplementationRegistry.newInstance(). 
          getDOMImplementation("LS"); 
      writer = new OutputStreamWriter(new FileOutputStream(filename)); 
      LSOutput lsout = ls.createLSOutput(); 
      lsout.setCharacterStream(writer); 
      /* 
      * If "doc" has been constructed by parsing an XML document, we 
      * should keep its encoding when serializing it; if it has been 
      * constructed in memory, its encoding has to be decided by the 
      * client code. 
      */ 
      lsout.setEncoding(doc.getXmlEncoding()); 
      LSSerializer serializer = ls.createLSSerializer(); 
      serializer.write(doc, lsout); 
     } catch (Exception e) { 
      throw new IOException(e); 
     } finally { 
      if (writer != null) writer.close(); 
     } 
    } 

les importations nécessaires:

import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.OutputStreamWriter; 
import java.io.Writer; 
import org.w3c.dom.Document; 
import org.w3c.dom.bootstrap.DOMImplementationRegistry; 
import org.w3c.dom.ls.DOMImplementationLS; 
import org.w3c.dom.ls.LSOutput; 
import org.w3c.dom.ls.LSSerializer; 

Je sais que c'est une vieille question à laquelle on a déjà répondu, mais je pense que les détails techniques pourraient aider quelqu'un.

0

J'ai essayé d'utiliser la bibliothèque LSSerializer et je n'ai pas réussi à obtenir quoi que ce soit en termes de conservation du Doctype. Ceci est la solution que Stephan a probablement utilisé Note: Ceci est scala mais utilise une bibliothèque java si juste convertir votre code

import com.sun.org.apache.xml.internal.serialize.{OutputFormat, XMLSerializer} 
def transformXML(root: Element, file: String): Unit = { 
    val doc = root.getOwnerDocument 
    val format = new OutputFormat(doc) 
    format.setIndenting(true) 
    val writer = new OutputStreamWriter(new FileOutputStream(new File(file))) 
    val serializer = new XMLSerializer(writer, format) 
    serializer.serialize(doc) 

    } 
Questions connexes