2011-07-10 134 views
16

我需要解析连续的格式良好的XML元素,我只向其提供已构建的java.io.Reader对象。这些元素不包含在根元素中,也不用像<?xml version="1.0"?>"这样的XML头部,而是其他有效的XML。解析没有根元素的XML流

使用Java org.xml.sax.XMLReader类不起作用,因为XML Reader希望解析格式良好的XML,从一个封闭的根元素开始。因此,它只是读取流中的第一个元素,它将其视为根,并且会在下一个元素中失败,其典型格式为

org.xml.sax.SAXParseException:文档后面的标记根元素必须是良构的。

对于不包含根元素的文件,但如果这样的元素不存在或无法被定义(和被调用,比如说,MyRootElement),一个可以做类似如下:

 Strint path = <the full path to the file>; 

     XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); 

     StringBuilder buffer = new StringBuilder(); 

     buffer.append("<?xml version=\"1.0\"?>\n"); 
     buffer.append("<!DOCTYPE MyRootElement "); 
     buffer.append("[<!ENTITY data SYSTEM \"file:///"); 
     buffer.append(path); 
     buffer.append("\">]>\n"); 
     buffer.append("<MyRootElement xmlns:...>\n"); 
     buffer.append("&data;\n"); 
     buffer.append("</MyRootElement>\n"); 

     InputSource source = new InputSource(new StringReader(buffer.toString())); 

     xmlReader.parse(source); 

我已经通过将java.io.Reader输出的一部分保存到文件来测试上述内容,并且它可以正常工作。但是,这种方法不适用于我的情况,因为传递给我的代码的对象java.io.Reader已经构建,所以不能插入这些额外信息(XML标头,根元素)。

本质上,我正在寻找“零碎的XML解析”。所以,我的问题是,可以使用标准的Java API(包括org.sax.xml.*java.xml.*包)来完成吗?

+0

可以参考[解决“的根元素下面的文档中的标记必须很好形成的”异常(http://opensourceforgeeks.blogspot.in/2015/01/resolving-markup-in- document-following.html) –

回答

12

的SequenceInputStream就派上用场了:

SAXParserFactory saxFactory = SAXParserFactory.newInstance(); 
    SAXParser parser = saxFactory.newSAXParser(); 

    parser.parse(
     new SequenceInputStream(
      Collections.enumeration(Arrays.asList(
      new InputStream[] { 
       new ByteArrayInputStream("<dummy>".getBytes()), 
       new FileInputStream(file),//bogus xml 
       new ByteArrayInputStream("</dummy>".getBytes()), 
      })) 
     ), 
     new DefaultHandler() 
    ); 
3

只需插入虚拟根元素。我能想到的最优雅的解决方案是创建自己的InputStream或Reader,它包装普通的InputSteam/Reader,并在您首次调用read()/ readLine()时返回虚拟<dummyroot>,然后返回负载流的结果。这应该满足SAX解析器。

9

您可以将您的给定Reader包装在您实施的FilterReader子类中,以做更多或更少的工作。

编辑:

虽然这类似于实现自己的Reader委托给其他几个答案给定Reader对象的建议,只是在FilterReader所有的方法都必须重写,所以你可能不会从使用超类获得太多收益。

其他建议的一个有趣变化可能是实现一个SequencedReader,其中包含多个Reader对象,并在用完时顺序移至下一个。然后,您可以传递StringReader对象,其中包含要添加的根的起始文本,原始Reader和另一个StringReader以及结束标记。

3

您可以创建自己的读者委托给提供的阅读器,就像这样:

final Reader reader = <whatever you are getting>; 

Reader wrappedReader = new Reader() 
{ 
    Reader readerCopy = reader; 
    String start = "<?xml version=\"1.0\"?><MyRootElement>"; 
    String end = "</MyRootElement>"; 
    int index; 

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

    @Override 
    public int read(char[] cbuf, int off, int len) throws IOException 
    { 
     // You'll have to get the logic right here - this is only placeholder code 

     if (index < start.length()) 
     { 
      // Copy from start to cbuf 
     } 
     int result = readerCopy.read(cbuf, off, len); 

     if (result == -1) { 
      // Copy from end 
     } 

     index += len; 

     return result; 
    } 
}; 

你必须填写逻辑首先从start读,然后委托给读者中间,最后当读者为空时,从end读取。

虽然这种方法可行。

+0

但是真的没有任何可以读取“碎片化”XML的XML解析类吗? – PNS

5

您可以编写自己的Reader-Implementation来封装您给出的Reader实例。这个新的Reader应该在您的示例代码中执行您正在做的事情,提供标头和根元素,然后提供底层读取器的数据,最后提供结束根标签。通过这种方式,您可以为XML解析器提供有效的XML流,并且可以使用传递给您的代码的Reader对象。

+0

+1伟大的思想思想相似(尽管我的思想是在你的前1分钟:)) – Bohemian

+0

+1给你们两个。直接实现读取器可能比试图在我的响应中继承FilterReader更好。 –

2

答案3部作品,但对我来说我必须做的创造从的SequenceInputStream的InputSource的额外步骤。

XMLReader xmlReader = saxParser.getXMLReader(); 
xmlReader.setContentHandler((ContentHandler) this); 
// Trying to add root element 
Enumeration<InputStream> streams = Collections.enumeration(
    Arrays.asList(new InputStream[] { 
     new ByteArrayInputStream("<TopNode>".getBytes()), 
     new FileInputStream(xmlFile),//bogus xml 
     new ByteArrayInputStream("</TopNode>".getBytes()), 
})); 
InputSource is = new InputSource(seqStream); 
xmlReader.parse(is); 
+0

通常回答是重新排序的,所以“答案3”是相对的,你的意思是什么答案? – emecas

+0

我的意思是用户给出的答案656449 –

+0

不编译 - notice'seqStream'没有在任何地方定义,重命名为流将为新的InputSource生成一个'没有合适的构造函数'错误。 –