2010-03-09 26 views
10

Comment puis-je forcer un analyseur SAX (spécifiquement, Xerces en Java) à utiliser une DTD lors de l'analyse d'un document sans avoir doctype dans le document d'entrée? Est-ce seulement possible?Comment puis-je forcer un analyseur SAX à utiliser une DTD si elle n'est pas spécifiée dans le fichier d'entrée?

Voici quelques détails de mon scénario:

Nous avons un tas de documents XML qui se conforment à la même DTD qui sont générés par plusieurs systèmes différents (dont aucun je peux changer). Certains de ces systèmes ajoutent un doctype à leurs documents de sortie, d'autres non. Certains utilisent des entités de caractères nommées, d'autres non. Certains utilisent des entités de caractères nommées sans déclarer de doctype. Je sais que ce n'est pas casher, mais c'est ce que je dois travailler.

Je travaille sur un système qui a besoin d'analyser ces fichiers en Java. Actuellement, il gère les cas ci-dessus en lisant d'abord dans le document XML en tant que flux, en essayant de détecter si un type de document est défini et en ajoutant une déclaration de type doctype si elle n'est pas déjà présente. Le problème est que ce code est bogué, et je voudrais le remplacer par quelque chose de plus propre.

Les fichiers sont volumineux, donc Je ne peux pas utiliser une solution DOM. J'essaie également de résoudre les entités de caractères, donc n'aide pas à utiliser un schéma XML.

Si vous avez une solution, pourriez-vous la publier directement au lieu de la lier? Stack Overflow n'est pas très bon si dans le futur il y a une solution correcte avec un lien mort.

Répondre

1

Je pense qu'il n'est pas sain de définir DOCTYPE, si le document n'en a pas. La solution possible est d'écrire faux, comme vous le faites déjà. Si vous utilisez SAX, vous pouvez utiliser cette fausse InputStream et fausse implémentation de DefaultHandler. (ne fonctionnera que pour le codage 1 octet latin1)

Je connais cette solution aussi moche, mais elle ne fonctionne qu'avec de gros flux de données.

Voici du code.

private enum State {readXmlDec, readXmlDecEnd, writeFakeDoctipe, writeEnd}; 

private class MyInputStream extends InputStream{ 

    private final InputStream is; 
    private StringBuilder sb = new StringBuilder(); 
    private int pos = 0; 
    private String doctype = "<!DOCTYPE register SYSTEM \"fake.dtd\">"; 
    private State state = State.readXmlDec; 

    private MyInputStream(InputStream source) { 
     is = source; 
    } 
    @Override 
    public int read() throws IOException { 
     int bit; 

     switch (state){ 
      case readXmlDec: 
       bit = is.read(); 
       sb.append(Character.toChars(bit)); 
       if(sb.toString().equals("<?xml")){ 
        state = State.readXmlDecEnd; 
       } 
       break; 
      case readXmlDecEnd: 
       bit = is.read(); 
       if(Character.toChars(bit)[0] == '>'){ 
        state = State.writeFakeDoctipe; 
       } 
       break; 
      case writeFakeDoctipe: 
       bit = doctype.charAt(pos++); 
       if(doctype.length() == pos){ 
        state = State.writeEnd; 
       } 
       break; 
      default: 
       bit = is.read(); 
       break; 
     } 
     return bit; 
    } 

    @Override 
    public void close() throws IOException { 
     super.close(); 
     is.close(); 
    } 
} 

private static class MyHandler extends DefaultHandler { 

    @Override 
    public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { 
     System.out.println("resolve "+ systemId); 
     // get real dtd 
     InputStream is = ClassLoader.class.getResourceAsStream("/register.dtd"); 
     return new InputSource(is); 
    } 

... // rest of code 
}