【问题标题】:Parsing an XML stream with no root element解析没有根元素的 XML 流
【发布时间】:2011-10-02 05:11:03
【问题描述】:

我需要解析一个连续的格式良好的 XML 元素流,我只得到一个已经构造好的 java.io.Reader 对象。这些元素没有包含在根元素中,也没有以像 <?xml version="1.0"?>" 这样的 XML 标头作为前缀,而是在其他方面是有效的 XML。

使用 Java org.xml.sax.XMLReader 类不起作用,因为 XML 阅读器希望从封闭的根元素开始解析格式良好的 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 输出保存到文件来测试上述内容,并且它可以工作。但是,这种方法不适用于我的情况,并且无法插入此类额外信息(XML 标头、根元素),因为传递给我的代码的 java.io.Reader 对象已经构建。

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

【问题讨论】:

标签: java xml sax xmlreader


【解决方案1】:

只需插入虚拟根元素。我能想到的最优雅的解决方案是创建自己的 InputStream 或 Reader 包装常规 InputSteam/Reader 并在您第一次调用它的 read() / readLine() 时返回虚拟 &lt;dummyroot&gt; 然后返回有效负载流的结果.这应该满足 SAX 解析器。

【讨论】:

    【解决方案2】:

    您可以将给定的Reader 包装在您实现的FilterReader 子类中,以或多或少地执行您在此处所做的事情。

    编辑:

    虽然这类似于实现您自己的 Reader 委托给由其他几个答案给出的给定 Reader 对象的提议,但 FilterReader 中的几乎所有方法都必须被覆盖,所以您可能不会从使用超类中获益良多。

    其他提议的一个有趣变化可能是实现一个SequencedReader,它包装多个Reader 对象,并在一个用完时转移到序列中的下一个。然后,您可以传入一个 StringReader 对象,其中包含要添加的根的起始文本、原始 Reader 和另一个带有结束标记的 StringReader

    【讨论】:

      【解决方案3】:

      您可以创建自己的 Reader 并委托给提供的 Reader,如下所示:

      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解析类吗?
      【解决方案4】:

      您可以编写自己的 Reader-Implementation 来封装您获得的 Reader-instance。这个新的阅读器应该做你在示例代码中所做的事情,提供标题和根元素,然后是来自底层阅读器的数据,最后是结束根标记。通过这种方式,您可以向 XML 解析器提供有效的 XML 流,并且还可以使用传递给代码的 Reader 对象。

      【讨论】:

      • +1 伟大的思想都一样(虽然我的想法比你早 1 分钟 :))
      • +1 给你们俩。直接实现 Reader 可能比在我的回复中尝试继承 FilterReader 更好。
      【解决方案5】:

      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()
          );
      

      【讨论】:

        【解决方案6】:

        This answer 对我有用,但我必须执行从SequenceInputStream 创建输入源的额外步骤。

        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”是相对的,你的意思是什么答案?
        • 我的意思是user656449给出的答案
        • 不编译 - 注意 'seqStream' 未在任何地方定义,重命名为流会为新 InputSource 生成“没有合适的构造函数”错误。
        猜你喜欢
        • 1970-01-01
        • 2012-06-28
        • 1970-01-01
        • 2019-10-13
        • 2012-11-17
        • 1970-01-01
        • 2018-02-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多