1

我一直在开发一个特定的 XML 解析器来解析大量的 XML 。

我的问题是我很困惑如何解析嵌套在其他 XML 值中的 XML 标记。我的输入文件看起来像这样。

<main>
<step>
    <para>Calculate the values from the pool</para>
</step>
<step>
        <para>Use these(<internalRef id ="003" xlink:actuate="onRequest" xlink:show="replace" xlink:href="max003"/>) values finally</para>
</step>
</main>

我能够使用 xpath 获取第一步标记的值。我的问题是如何使用 xpath 获取第二步值,或者更确切地说如何识别新标签何时在值标签中开始。

例如,我的第二步 xpath 正在返回这个结果 -最后使用这些 () 值

我的目标是 最终使用这些(max003)值

max003值必须取自xlink:href

另外- 我可以通过编写单独的 xpath 来获得 id 、actuate、show 的各个值。我的问题是我需要 在获得xlink:href值(即max003 )之后将max003值填充到这些之后和值之前的括号内发送以进行显示。 所以我正在寻找一种方法来识别子节点 ID 的开始位置和时间?以及将其填充在括号内的机制。

4

3 回答 3

2

仅使用 XPath 将无法做到这一点。您所拥有的是混合内容 XML,这意味着一个元素可能同时包含文本值和子元素。您一次只能使用 XPath 引用其中一个,并且您也不能仅仅连接从多个 XPath 表达式中获得的内容,因为文本值可能会围绕您在示例中声明的子元素。

我建议您使用 XSLT 转换文档,然后像现在一样使用 XPath 查询转换后的文档。另一种方法是编写自己的解析器,它能够正确处理嵌套元素。

这个 XSLT 可能会为您工作(尚未彻底测试):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xlink="http://www.w3.org/1999/xlink">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="internalRef">
        <xsl:value-of select="@xlink:href"/>
    </xsl:template>
</xsl:stylesheet>

当然,您随后需要使用 XSLT 处理器来转换您的原始文档。

解析器可能看起来像这样(请注意,这只是 StAX 解析器的框架代码):

import java.io.StringReader;
import java.util.Iterator;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

public class ExampleStAXParser {

    private static final int STATE_UNDEFINED = 1;
    private static final int STATE_MAIN = 2;
    private static final int STATE_STEP = 3;
    private static final int STATE_PARA = 4;

    private static final String EL_MAIN = "main";
    private static final String EL_STEP = "step";
    private static final String EL_PARA = "para";
    private static final String EL_INTERNAL_REF = "internalRef";
    private static final String ATT_HREF = "href";

    private int state = STATE_UNDEFINED;
    private String characters;

    public void parse(String xmlString) throws XMLStreamException, Exception {


        XMLEventReader reader = null;
        try {
            if (xmlString == null || xmlString.isEmpty()) {
                throw new IllegalArgumentException("Illegal initializiation (xmlString is null or empty)");
            }
            StringReader stringReader = new StringReader(xmlString);
            XMLInputFactory inputFact = XMLInputFactory.newInstance();
            XMLStreamReader streamReader = inputFact.createXMLStreamReader(stringReader);
            reader = inputFact.createXMLEventReader(streamReader);

            while (reader.hasNext()) {
                XMLEvent event = reader.nextEvent();

                if (event.isCharacters()) {
                    characters(event);
                }
                if (event.isStartElement()) {
                    startElement(event);
                    // handle attributes
                    Iterator<Attribute> attributes = event.asStartElement().getAttributes();
                    while(attributes.hasNext()) {
                        attribute(attributes.next());
                    }
                }
                if (event.isEndElement()) {
                    endElement(event);
                }
                if (event.isStartDocument()) {
                    startDocument(event);
                }
                if (event.isEndDocument()) {
                    endDocument(event);
                }

            }            
        } catch (XMLStreamException ex) {
            throw ex;
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (XMLStreamException ex) {
            }
        }
    }

    private void attribute(XMLEvent event) throws Exception {
        if (state == STATE_PARA) {
            Attribute attr = (Attribute) event;
            String name = attr.getName().getLocalPart();
            if (ATT_HREF.equals(name)) {
                if (characters == null) {
                    characters = attr.getValue();
                } else {
                     characters += attr.getValue();
                }
            }
        } else
            throw new Exception("unexpected attribute");
    }

    private void characters(XMLEvent event) throws Exception {
        Characters asCharacters = event.asCharacters();
        if (asCharacters.isWhiteSpace())
            return;
        if (state == STATE_PARA) {            
            if (characters == null) {
                characters = asCharacters.getData();
            } else {
                 characters += asCharacters.getData();
            }
        } else
            throw new Exception("unexpected attribute");
    }

    private void startElement(XMLEvent event) throws Exception {
        StartElement startElement = event.asStartElement();
        String name = startElement.getName().getLocalPart();
        switch (state) {
            case STATE_UNDEFINED:
                if (name.equals(EL_MAIN)) {
                    state = STATE_MAIN;
                    System.out.println("Element: " + name);
                } else
                    throw new Exception("unexpected element");
                break;
            case STATE_MAIN:
                if (name.equals(EL_STEP)) {
                    state = STATE_STEP;
                    System.out.println("Element: " + name);
                } else
                    throw new Exception("unexpected element");
                break;
            case STATE_STEP:
                if (name.equals(EL_PARA)) {
                    state = STATE_PARA;
                    System.out.println("Element: " + name);
                } else
                    throw new Exception("unexpected element");
                break;
            case STATE_PARA:
                if (name.equals(EL_INTERNAL_REF)) {
                    System.out.println("Element: " + name);
                } else
                    throw new Exception("unexpected element");
                break;
            default:
                throw new Exception("unexpected element");
        }
    }

    private void endElement(XMLEvent event) throws Exception {
        EndElement endElement = event.asEndElement();
        String name = endElement.getName().getLocalPart();
        switch (state) {
            case STATE_MAIN:
                if (name.equals(EL_MAIN)) {
                    state = STATE_UNDEFINED;
                } else
                    throw new Exception("unexpected element");
                break;
            case STATE_STEP:
                if (name.equals(EL_STEP)) {
                    state = STATE_MAIN;
                } else
                    throw new Exception("unexpected element");
                break;
            case STATE_PARA:
                if (name.equals(EL_INTERNAL_REF)) {
                    // do nothing
                } else if (name.equals(EL_PARA)) {
                    System.out.println("Value: " + String.valueOf(characters));
                    characters = null;
                    state = STATE_STEP;
                } else
                    throw new Exception("unexpected element");
                break;
            default:
                throw new Exception("unexpected element");
        }
    }

    private void startDocument(XMLEvent event) {
        System.out.println("Parsing started");
    }

    private void endDocument(XMLEvent event) {
        System.out.println("Parsing ended");
    }

    public static void main(String[] argv) throws XMLStreamException, Exception {
        String xml = "";
        xml += "<main>";
        xml += "<step>";
        xml += "    <para>Calculate the values from the pool</para>";
        xml += "</step>";
        xml += "<step>";
        xml += "        <para>Use these(<internalRef id =\"003\" actuate=\"onRequest\" show=\"replace\" href=\"max003\"/>) values finally</para>";
        xml += "</step>";
        xml += "</main>";

        ExampleStAXParser parser = new ExampleStAXParser();
        parser.parse(xml);
    }
}
于 2012-09-05T08:07:43.817 回答
2

此 Xpath 表达式的评估:

 concat(/*/step[2]/para/text()[1],
        /*/step[2]/para/internalRef/@xlink:href,
        /*/step[2]/para/text()[2])

在提供的 XML 文档上(更正为命名空间格式):

<main xmlns:xlink="Undefined namespace">
    <step>
        <para>Calculate the values from the pool</para>
    </step>
    <step>
        <para>Use these(<internalRef id ="003" xlink:actuate="onRequest" xlink:show="replace" xlink:href="max003"/>) values finally</para>
    </step>
</main>

产生想要的结果

Use these(max003) values finally

请注意:您需要在 XPath API 中“注册 xlink 命名空间”,以便在没有错误的情况下评估此 XPath 表达式。

基于 XSLT 的验证

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xlink="Undefined namespace">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "concat(/*/step[2]/para/text()[1],
           /*/step[2]/para/internalRef/@xlink:href,
           /*/step[2]/para/text()[2])
     "/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档(上图)时,将评估 Xpath 表达式并将此评估的结果复制到输出

Use these(max003) values finally
于 2012-09-05T12:35:48.700 回答
1

据我所知,我认为您的解析器看起来有点像

step
 +- para
     +-id

然后将“文本”内容包装在一起以提取该 id 节点......

(纯属猜测)

更新

如果我只是简单地遍历节点树(列出每个孩子),这就是我得到的

 main
  step
    para
      #text - Calculate the values from the pool
  step
    para
      #text - Use these(
      id
      #text - ) values finally

这意味着“id”是“para”的孩子

于 2012-09-05T07:39:42.313 回答