2010-03-09 10 views
10

Làm cách nào để buộc trình phân tích cú pháp SAX (cụ thể là Xerces trong Java) sử dụng DTD khi phân tích cú pháp tài liệu mà không cần bất kỳ tài liệu nào trong tài liệu đầu vào? Điều này thậm chí có thể?Làm cách nào để buộc trình phân tích cú pháp SAX sử dụng DTD nếu một tệp không được chỉ định trong tệp đầu vào?

Dưới đây là một số chi tiết của kịch bản của tôi:

Chúng tôi có một loạt các tài liệu XML mà phù hợp với các DTD tương tự được tạo ra bởi nhiều hệ thống khác nhau (không ai trong số đó tôi có thể thay đổi). Một số các hệ thống này thêm một loại tài liệu vào tài liệu đầu ra của họ, những người khác thì không. Một số sử dụng có tên là các thực thể ký tự, một số thì không. Một số sử dụng các thực thể ký tự có tên mà không khai báo một loại tài liệu. Tôi biết đó không phải là kosher, nhưng đó là những gì tôi phải làm việc với.

Tôi đang làm việc trên hệ thống cần phân tích các tệp này trong Java. Hiện tại, nó xử lý các trường hợp trên bằng cách lần đầu tiên đọc trong tài liệu XML dưới dạng luồng, cố gắng phát hiện xem nó có một DOCTYPE được xác định hay không và thêm một khai báo kiểu doctype nếu chưa có. Vấn đề là mã này là lỗi, và tôi muốn thay thế nó bằng một cái gì đó sạch hơn.

Các tệp lớn, vì vậy Tôi không thể sử dụng giải pháp dựa trên DOM. Tôi cũng đang cố gắng nhận các đối tượng ký tự được giải quyết, vì vậy, không giúp sử dụng Lược đồ XML.

Nếu bạn có giải pháp, bạn có thể vui lòng đăng trực tiếp thay vì liên kết với nó không? Nó không làm Stack tràn tốt nếu trong tương lai có một giải pháp đúng với một liên kết chết.

Trả lời

1

Tôi nghĩ rằng không có cách nào tốt để đặt DOCTYPE, nếu tài liệu không có. Giải pháp có thể là viết giả, như bạn đã làm. Nếu bạn đang sử dụng SAX, bạn có thể sử dụng InputStream giả mạo này và thực thi DefaultHandler giả mạo. (sẽ chỉ hoạt động đối với mã hóa 1 byte latin)

Tôi biết giải pháp này cũng xấu xí, nhưng chỉ một giải pháp hoạt động tốt với luồng dữ liệu lớn.

Dưới đây là một số mã.

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 
}