2010-03-09 4 views
10

Wie kann ich einen SAX-Parser (speziell Xerces in Java) zwingen, eine DTD zu verwenden, wenn ein Dokument ohne beliebigen Doctype im Eingabedokument analysiert wird? Ist das überhaupt möglich?Wie kann ich einen SAX-Parser zwingen, eine DTD zu verwenden, wenn keine in der Eingabedatei angegeben ist?

Hier sind einige weitere Details meiner Szenario:

Wir haben eine Reihe von XML-Dokumenten, die mit der gleichen DTD entsprechen, die von mehreren verschiedenen Systemen (von denen keine die ich ändern kann) erzeugt werden. Einige dieser Systeme fügen ihren Ausgabedokumenten einen Doctype hinzu, andere nicht. Einige verwenden benannte Zeichenentitäten, andere nicht. Einige verwenden benannte Zeichenentitäten, ohne einen Doctype zu deklarieren. Ich weiß, dass das nicht koscher ist, aber ich muss damit arbeiten.

Ich arbeite an einem System, das diese Dateien in Java analysieren muss. Momentan behandelt es die obigen Fälle, indem es zuerst das XML-Dokument als Stream liest, versucht, zu erkennen, ob es einen Doctype definiert hat, und eine Doctype-Deklaration hinzufügt, falls noch keine vorhanden ist. Das Problem ist, dass dieser Code fehlerhaft ist, und ich möchte ihn durch etwas saubereres ersetzen.

Die Dateien sind groß, so Ich kann keine DOM-basierte Lösung verwenden. Ich versuche auch, Zeichenentitäten aufgelöst zu bekommen, so dass es nicht hilft ein XML-Schema zu verwenden.

Wenn Sie eine Lösung haben, können Sie sie bitte direkt posten, anstatt sie zu verknüpfen? Es tut Stack Overflow nicht gut, wenn es in der Zukunft eine korrekte Lösung mit einem toten Link gibt.

Antwort

1

Ich denke, es ist keine vernünftige Art, DOCTYPE zu setzen, wenn das Dokument nicht eins hat. Mögliche Lösung ist, schreiben Sie einen falschen, wie Sie bereits tun. Wenn Sie SAX verwenden, können Sie diesen falschen InputStream und die falsche DefaultHandler-Implementierung verwenden. (funktioniert nur für latin1 Ein-Byte-Kodierung)

Ich kenne diese Lösung auch hässlich, aber es funktioniert nur gut mit großen Datenströmen.

Hier einige 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 
} 
Verwandte Themen