解析没有根元素的XML流
我需要解析连续的格式良好的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.*
包)来完成吗?
的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()
);
只需插入虚拟根元素。我能想到的最优雅的解决方案是创建自己的InputStream或Reader,它包装普通的InputSteam/Reader,并在您首次调用read()/ readLine()时返回虚拟<dummyroot>
,然后返回负载流的结果。这应该满足SAX解析器。
您可以将您的给定Reader
包装在您实施的FilterReader
子类中,以做更多或更少的工作。
编辑:
虽然这类似于实现自己的Reader
委托给其他几个答案给定Reader
对象的建议,只是在FilterReader
所有的方法都必须重写,所以你可能不会从使用超类获得太多收益。
其他建议的一个有趣变化可能是实现一个SequencedReader
,其中包含多个Reader
对象,并在用完时顺序移至下一个。然后,您可以传递StringReader
对象,其中包含要添加的根的起始文本,原始Reader
和另一个StringReader
以及结束标记。
您可以创建自己的读者委托给提供的阅读器,就像这样:
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
读取。
虽然这种方法可行。
但是真的没有任何可以读取“碎片化”XML的XML解析类吗? – PNS
您可以编写自己的Reader-Implementation来封装您给出的Reader实例。这个新的Reader应该在您的示例代码中执行您正在做的事情,提供标头和根元素,然后提供底层读取器的数据,最后提供结束根标签。通过这种方式,您可以为XML解析器提供有效的XML流,并且可以使用传递给您的代码的Reader对象。
+1伟大的思想思想相似(尽管我的思想是在你的前1分钟:)) – Bohemian
+1给你们两个。直接实现读取器可能比试图在我的响应中继承FilterReader更好。 –
答案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);
通常回答是重新排序的,所以“答案3”是相对的,你的意思是什么答案? – emecas
我的意思是用户给出的答案656449 –
不编译 - notice'seqStream'没有在任何地方定义,重命名为流将为新的InputSource生成一个'没有合适的构造函数'错误。 –
可以参考[解决“的根元素下面的文档中的标记必须很好形成的”异常(http://opensourceforgeeks.blogspot.in/2015/01/resolving-markup-in- document-following.html) –