【问题标题】:stax - get xml node as stringstax - 以字符串形式获取 xml 节点
【发布时间】:2011-05-20 02:32:00
【问题描述】:

xml 看起来像这样:

<statements>
   <statement account="123">
      ...stuff...
   </statement>
   <statement account="456">
      ...stuff...
   </statement>
</statements>

我正在使用 stax 一次处理一个“&lt;statement&gt;”并且我得到了它的工作。我需要将整个语句节点作为字符串获取,以便创建“123.xml”和“456.xml”,甚至可以将其加载到按帐户索引的数据库表中。

使用这种方法:http://www.devx.com/Java/Article/30298/1954

我想做这样的事情:

String statementXml = staxXmlReader.getNodeByName("statement");

//load statementXml into database

【问题讨论】:

  • 你的问题到底是什么?

标签: java xml extract stax


【解决方案1】:

我一直在谷歌上搜索,这似乎非常困难。

鉴于我的 xml,我认为它可能更简单:

StringBuilder buffer = new StringBuilder();
for each line in file {
   buffer.append(line)
   if(line.equals(STMT_END_TAG)){
      parse(buffer.toString())
      buffer.delete(0,buffer.length)
   }
 }

 private void parse(String statement){
    //saxParser.parse( new InputSource( new StringReader( xmlText ) );
    // do stuff
    // save string
 }

【讨论】:

    【解决方案2】:

    为什么不直接使用 xpath 呢?

    您可以有一个相当简单的 xpath 来获取所有“语句”节点。

    像这样:

    //statement
    

    编辑 #1:如果可能,请查看 dom4j。您可以读取字符串并相当简单地获取所有“语句”节点。

    编辑 #2:使用 dom4j,你会这样做: (来自他们的食谱)

    String text = "your xml here";
    Document document = DocumentHelper.parseText(text);
    
    public void bar(Document document) {
       List list = document.selectNodes( "//statement" );
       // loop through node data
    }
    

    【讨论】:

    • JDK/JRE 中还有标准 XPath 库:stackoverflow.com/questions/3939636/…
    • 发帖人明确提到了 StAX,所以我认为指向 dom4j 或其他库的指针对他没有多大帮助......
    • 鉴于 OP 从未提出过问题,因此使用 xPath 的建议与任何东西一样好。也许更好。
    【解决方案3】:

    Stax 是一种低级访问 API,它没有查找或递归访问内容的方法。但你实际上想做什么?您为什么要考虑使用 Stax?

    除了使用可以很好地与 XPath 配合使用的树模型(DOM、XOM、JDOM、Dom4j)之外,处理数据时的最佳选择通常是像 JAXB 这样的数据绑定库。有了它,您可以传递 Stax 或 SAX 阅读器并要求它将 xml 数据绑定到 Java bean 中,而不是弄乱 xml 处理 Java 对象。这通常更方便,而且通常相当性能。 较大文件的唯一技巧是您不想一次绑定整个内容,而是绑定每个子树(在您的情况下,一次一个“语句”)。 最简单的方法是迭代 Stax XmlStreamReader,然后使用 JAXB 进行绑定。

    【讨论】:

      【解决方案4】:

      您可以为此使用 StAX。您只需要将 XMLStreamReader 推进到语句的开始元素。检查帐户属性以获取文件名。然后使用 javax.xml.transform API 将 StAXSource 转换为包含 File 的 StreamResult。这将推进 XMLStreamReader,然后重复此过程。

      import java.io.File;
      import java.io.FileReader;
      import javax.xml.stream.XMLInputFactory;
      import javax.xml.stream.XMLStreamConstants;
      import javax.xml.stream.XMLStreamReader;
      import javax.xml.transform.Transformer;
      import javax.xml.transform.TransformerFactory;
      import javax.xml.transform.stax.StAXSource;
      import javax.xml.transform.stream.StreamResult;
      
      public class Demo {
      
          public static void main(String[] args) throws Exception  {
              XMLInputFactory xif = XMLInputFactory.newInstance();
              XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
              xsr.nextTag(); // Advance to statements element
      
              while(xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
                  TransformerFactory tf = TransformerFactory.newInstance();
                  Transformer t = tf.newTransformer();
                  File file = new File("out" + xsr.getAttributeValue(null, "account") + ".xml");
                  t.transform(new StAXSource(xsr), new StreamResult(file));
              }
          }
      
      }
      

      【讨论】:

      • 使用 while (xsr.nextTag...) 将失败。 xsr.nextTag() 的 stax 文档指出,如果 xsr.hasNext() 为 false 并调用下一个标记,则会引发异常。此外,在使用 xsr.nextTag() 时,如果遇到除空白字符以外的 COMMENT、PROCESSING_INSTRUCTION、START_ELEMENT、END_ELEMENT,则会引发异常。
      • 当我使用上面的代码时,我在线程 "main" net.sf.saxon.trans.XPathException: org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt is在不允许的地方插入一个节点。有什么想法吗?
      • 概念上调用 xsr.nextTag() 是错误的,因为如果“input.xml”不包含标题,XMLStreamReader 可能已经从正确的标签开始.尝试所有可能的情况,我总是收到错误:java.lang.IllegalStateException: Attempt to output end tag with no matching start tag. @t0r0X 解决方案是唯一对我有效的解决方案
      【解决方案5】:

      我有一个类似的任务,虽然最初的问题已经超过一年,但我找不到令人满意的答案。到目前为止,最有趣的答案是 Blaise Doughan 的答案,但我无法让它在我期望的 XML 上运行(也许底层解析器的一些参数可以改变它?)。这里是 XML,非常简单:

      <many-many-tags>
          <description>
              ...
              <p>Lorem ipsum...</p>
              Devils inside...
              ...
          </description>
      </many-many-tags>
      

      我的解决方案:

      public static String readElementBody(XMLEventReader eventReader)
          throws XMLStreamException {
          StringWriter buf = new StringWriter(1024);
      
          int depth = 0;
          while (eventReader.hasNext()) {
              // peek event
              XMLEvent xmlEvent = eventReader.peek();
      
              if (xmlEvent.isStartElement()) {
                  ++depth;
              }
              else if (xmlEvent.isEndElement()) {
                  --depth;
      
                  // reached END_ELEMENT tag?
                  // break loop, leave event in stream
                  if (depth < 0)
                      break;
              }
      
              // consume event
              xmlEvent = eventReader.nextEvent();
      
              // print out event
              xmlEvent.writeAsEncodedUnicode(buf);
          }
      
          return buf.getBuffer().toString();
      }
      

      使用示例:

      XMLEventReader eventReader = ...;
      while (eventReader.hasNext()) {
          XMLEvent xmlEvent = eventReader.nextEvent();
          if (xmlEvent.isStartElement()) {
              StartElement elem = xmlEvent.asStartElement();
              String name = elem.getName().getLocalPart();
      
              if ("DESCRIPTION".equals(name)) {
                  String xmlFragment = readElementBody(eventReader);
                  // do something with it...
                  System.out.println("'" + fragment + "'");
              }
          }
          else if (xmlEvent.isEndElement()) {
              // ...
          }
      }
      

      请注意,提取的 XML 片段将包含完整的提取正文内容,包括空格和 cmets。为代码简洁起见,已忽略按需过滤或使缓冲区大小可参数化:

      '
          <description>
              ...
              <p>Lorem ipsum...</p>
              Devils inside...
              ...
          </description>
          '
      

      【讨论】:

      • 有没有办法在没有命名空间的情况下打印字符串?
      • 我不确定我是否理解您的问题,什么命名空间?能举个例子吗?
      【解决方案6】:

      我遇到了类似的问题并找到了解决方案。 我使用了@t0r0X 提出的解决方案,但它在 Java 11 的当前实现中效果不佳,xmlEvent.writeAsEncodedUnicode 方法在结果 XML 片段中创建了起始元素(在StartElementEvent 类中)的无效字符串表示,所以我不得不修改它,但它似乎运行良好,我可以通过 DOM 和 JaxBMarshaller 将片段解析到特定数据容器来立即验证。

      就我而言,我有巨大的结构

      <Orders>
         <ns2:SyncOrder xmlns:ns2="..." xmlns:ns3="....." ....>
            .....
         </ns2:SyncOrder>
         <ns2:SyncOrder xmlns:ns2="..." xmlns:ns3="....." ....>
            .....
         </ns2:SyncOrder>
         ...
      </Orders>
      

      在数百兆的文件中(大量重复的“SyncOrder”结构),因此使用DOM会导致内存消耗大,评估慢。因此,我使用 StAX 将巨大的 XML 拆分为更小的 XML 片段,我已经使用 DOM 对其进行了分析,并使用了从元素 SyncOrder 的 xsd 定义生成的 JaxbElements(我从 web 服务获得的这个基础设施,它使用相同的结构,但并不重要)。

      在这段代码中可以看到XML片段已经创建并可以使用的地方,我直接在其他处理中使用它......

      private static <T> List<T> unmarshallMultipleSyncOrderXmlData(
              InputStream aOrdersXmlContainingSyncOrderItems,
              Function<SyncOrderType, T> aConversionFunction) throws XMLStreamException, ParserConfigurationException, IOException, SAXException {
      
          DocumentBuilderFactory locDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
          locDocumentBuilderFactory.setNamespaceAware(true);
          DocumentBuilder locDocBuilder = locDocumentBuilderFactory.newDocumentBuilder();
      
          List<T> locResult = new ArrayList<>();
          XMLInputFactory locFactory = XMLInputFactory.newFactory();
          XMLEventReader locReader = locFactory.createXMLEventReader(aOrdersXmlContainingSyncOrderItems);
      
          boolean locIsInSyncOrder = false;
          QName locSyncOrderElementQName = null;
          StringWriter locXmlTextBuffer = new StringWriter();
          int locDepth = 0;
          while (locReader.hasNext()) {
      
              XMLEvent locEvent = locReader.nextEvent();
      
              if (locEvent.isStartElement()) {
                  if (locDepth == 0 && Objects.equals(locEvent.asStartElement().getName().getLocalPart(), "Orders")) {
                      locDepth++;
                  } else {
                      if (locDepth <= 0)
                          throw new IllegalStateException("There has been passed invalid XML stream intot he function. "
                                                                                          + "Expecting the element 'Orders' as the root alament of the document, but found was '"
                                                                                          + locEvent.asStartElement().getName().getLocalPart() + "'.");
                      locDepth++;
                      if (locSyncOrderElementQName == null) {
                          /* First element after the "Orders" has passed, so we retrieve
                           * the name of the element with the namespace prefix: */
                          locSyncOrderElementQName = locEvent.asStartElement().getName();
                      }
                      if(Objects.equals(locEvent.asStartElement().getName(), locSyncOrderElementQName)) {
                          locIsInSyncOrder = true;
                      }
                  }
              } else if (locEvent.isEndElement()) {
                  locDepth--;
                  if(locDepth == 1 && Objects.equals(locEvent.asEndElement().getName(), locSyncOrderElementQName)) {
                      locEvent.writeAsEncodedUnicode(locXmlTextBuffer);
                      /* at this moment the call of locXmlTextBuffer.toString() gets the complete fragment 
                       * of XML containing the valid SyncOrder element, but I have continued to other processing,
                       * which immediatelly validates the produced XML fragment is valid and passes the values 
                       * to communication object: */
                      Document locDocument = locDocBuilder.parse(new ByteArrayInputStream(locXmlTextBuffer.toString().getBytes()));
                      SyncOrderType locItem = unmarshallSyncOrderDomNodeToCo(locDocument);
                      locResult.add(aConversionFunction.apply(locItem));
                      locXmlTextBuffer = new StringWriter();
                      locIsInSyncOrder = false;
                  }
              }
              if (locIsInSyncOrder) {
                  if (locEvent.isStartElement()) {
                      /* here replaced the standard implementation of startElement's method writeAsEncodedUnicode: */ 
                      locXmlTextBuffer.write(startElementToStrng(locEvent.asStartElement()));
                  } else {
                      locEvent.writeAsEncodedUnicode(locXmlTextBuffer);
                  }
              }
          }
          return locResult;
      }
      
      private static String startElementToString(StartElement aStartElement) {
      
          StringBuilder locStartElementBuffer = new StringBuilder();
      
          // open element
          locStartElementBuffer.append("<");
          String locNameAsString = null;
          if ("".equals(aStartElement.getName().getNamespaceURI())) {
              locNameAsString = aStartElement.getName().getLocalPart();
          } else if (aStartElement.getName().getPrefix() != null
                  && !"".equals(aStartElement.getName().getPrefix())) {
              locNameAsString = aStartElement.getName().getPrefix()
                      + ":" + aStartElement.getName().getLocalPart();
          } else {
              locNameAsString = aStartElement.getName().getLocalPart();
          }
      
          locStartElementBuffer.append(locNameAsString);
      
          // add any attributes
          Iterator<Attribute> locAttributeIterator = aStartElement.getAttributes();
          Attribute attr;
          while (locAttributeIterator.hasNext()) {
              attr = locAttributeIterator.next();
              locStartElementBuffer.append(" ");
              locStartElementBuffer.append(attributeToString(attr));
          }
      
          // add any namespaces
          Iterator<Namespace> locNamespaceIterator = aStartElement.getNamespaces();
          Namespace locNamespace;
          while (locNamespaceIterator.hasNext()) {
              locNamespace = locNamespaceIterator.next();
              locStartElementBuffer.append(" ");
              locStartElementBuffer.append(attributeToString(locNamespace));
          }
      
          // close start tag
          locStartElementBuffer.append(">");
      
          // return StartElement as a String
          return locStartElementBuffer.toString();
      }
      
      private static String attributeToString(Attribute aAttr) {
          if( aAttr.getName().getPrefix() != null && aAttr.getName().getPrefix().length() > 0 )
              return aAttr.getName().getPrefix() + ":" + aAttr.getName().getLocalPart() + "='" + aAttr.getValue() + "'";
          else
              return aAttr.getName().getLocalPart() + "='" + aAttr.getValue() + "'";
      }
      
      public static SyncOrderType unmarshallSyncOrderDomNodeToCo(
              Node aSyncOrderItemNode) {
          Source locSource = new DOMSource(aSyncOrderItemNode);
          Object locUnmarshalledObject = getMarshallerAndUnmarshaller().unmarshal(locSource);
          SyncOrderType locCo = ((JAXBElement<SyncOrderType>) locUnmarshalledObject).getValue();
          return locCo;
      }
      

      【讨论】:

        猜你喜欢
        • 2013-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-30
        • 2011-06-06
        • 2014-04-19
        • 1970-01-01
        相关资源
        最近更新 更多