【问题标题】:How to fix xml document data by retireving xpaths如何通过检索 xpaths 来修复 xml 文档数据
【发布时间】:2015-08-24 18:18:24
【问题描述】:

我想获取包含重复元素的 XML 文档的 XPath。

示例:

<Return>
  <ReturnData>
    <Person>
      <Name>Yohanna</Name>
    </Person>
    <Person>
      <Name>Jacoub</Name>
    </Person>
  </ReturnData>
</Return>

我想退货:

1. /Return/ReturnData/Person[1]/Name=Yohanna
2. /Return/ReturnData/Person[2]/Name=Jacoub

我有一个实现,它可以为我检索任何 XML 文档的 XPath,但是我有重复 XPath 的问题,我不确定如何索引 XPath 是唯一的,因此我可以将值分配给它作为 Key/Value 对正如我在上面展示的那样。我想我应该使用 Map 数据结构,但我不确定如何做到这一点。

这是我的代码

public List<String> getXPaths ( InputStream stream ) throws ParserException {
    Document document = XMLUtils.getDocument(  stream );
    return getXPaths( document.getDocumentElement() );
}

public List<String> getXPaths ( Node node ) {
    List<String> xpaths = iterate( node, "");
    return xpaths;
}


public List<String> iterate ( Node node, String parentPath )  {

    List<String> xpaths = new ArrayList<String>();

    if ( node.getNodeType() == Node.ELEMENT_NODE ) {

        Element element = ( Element ) node;
        parentPath = parentPath + "/" +  element.getTagName();

        for ( int nIndex = 0; nIndex<node.getChildNodes().getLength(); nIndex++ ) {
            xpaths.addAll( iterate(node.getChildNodes().item(nIndex) , parentPath ) ) ;
        }
    }
    else if ( node.getNodeType() == Node.TEXT_NODE  ) {
        if (  node.getTextContent().trim().length() !=0 ) {
            logger.debug("XPath found : " + parentPath );
            xpaths.add( parentPath );
        }
    }
    else {
        logger.debug("Unknown node type for : " + node.getNodeName());
    }
    return xpaths;
}

目前,此代码仅返回一个未编入索引的 XPath 列表

输出:

/Return/ReturnData/Person/Name
/Return/ReturnData/Person/Name

我们将不胜感激。

另一个修改:

public String getFullXPathV2(Node n) {
    ...etc.

            while (null != prev_sibling) {

                if (prev_sibling.getNodeType() == node.getNodeType()) {
                    if (prev_sibling.getNodeName().equalsIgnoreCase(node.getNodeName())) { 
                        prev_siblings++;
                    }
                }
                prev_sibling = prev_sibling.getPreviousSibling();
            }

            // Edit here
            if(prev_siblings == 1) {
                continue;
            } 

            else 
                builder.append("[").append(prev_siblings).append("]");

        } 

        else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
            builder.append("/@");
            builder.append(node.getNodeName());
        }
    }
    return builder.toString();
}

输出:

[/Return/ReturnData/Person/Name = Yohanna, /Return/ReturnData/Person[2]/Name = Jacoub]

这似乎没问题,但是 /Return/ReturnData/Person/Name = Yohanna 它应该是 /Person[1] 来表示它第一次出现 Person 节点。

【问题讨论】:

    标签: java xml xpath


    【解决方案1】:

    在代码执行后,您拥有 xpath 值。您必须评估您的 xpath 表达式才能从此 xpath 中获取准确的索引和值。

    您可以将其保存到文件或集合中,然后再进行处理。当所有没有索引的 xpath 都保存到文件时,我将展示一些示例。并且会处理这个文件。

    代码应该如下:

    public final class XpathEvaluator {
        private DocumentBuilder builder;
        private Document document;
    
        public XpathEvaluator() {
            try {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                builder = factory.newDocumentBuilder();
            } catch (ParserConfigurationException e) {
                Logger.error("Can not create document builder for xpath ", e.getCause());
            }
        }
    
        public void loadFile(String filename) {
            Logger.operation("Load file for Xpath evaluation: " + filename);
            File file = new File(filename);
            try {
                document = builder.parse(file);
            } catch (SAXException | IOException e) {
                Logger.error("exception at loading xml file", e.getCause());
            }
        }
    
        public List<NodeSearchResult> getXpathSearchResults(String xpathExpression) {
            Logger.operation("Evaluate xpath expression: [" + xpathExpression + "]");
            List<NodeSearchResult> results = new ArrayList<>();
    
            XPathExpression expression;
            NodeList list = null;
            try {
                expression = getXPath().compile(xpathExpression);
                list = (NodeList) expression.evaluate(document, XPathConstants.NODESET);
            } catch (XPathExpressionException e) {
                Logger.error("XPathExpressionException during xpath evaluation", e.getCause());
            }
    
            for (int index = 0; index < list.getLength(); index++) {
                Node node = list.item(index);
                String nodeXPath = getFullXPath(node);
                String content = node.getTextContent();
    
                Logger.debug("Get NODE value: [" + content + "]");
                results.add(new NodeSearchResult(nodeXPath, content));
            }
    
            if (results.isEmpty()) { // log error if node result is empty
                Logger.warn("XPATH result is EMPTY, for next xpath [" + xpathExpression + "]");
                return Collections.emptyList();
            }
    
            Logger.operation("Xpath evaluation results: " + results);
            return results;
        }
    
        private String getFullXPath(Node n) {
            if (null == n)
                return null;
    
            Node parent;
            Stack<Node> hierarchy = new Stack<>();
            StringBuilder builder = new StringBuilder();
    
            hierarchy.push(n);
    
            switch (n.getNodeType()) {
                case Node.ATTRIBUTE_NODE:
                    parent = ((Attr) n).getOwnerElement();
                    break;
                case Node.ELEMENT_NODE:
                    parent = n.getParentNode();
                    break;
                case Node.DOCUMENT_NODE:
                    parent = n.getParentNode();
                    break;
                default:
                    throw new IllegalStateException("Unexpected Node type" + n.getNodeType());
            }
    
            while (null != parent
                    && parent.getNodeType() != Node.DOCUMENT_NODE
                    && !parent.getNodeName().equals("section")) {
                hierarchy.push(parent);
                parent = parent.getParentNode();
            }
    
            Object obj;
            while (!hierarchy.isEmpty() && null != (obj = hierarchy.pop())) {
                Node node = (Node) obj;
    
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    builder.append("/").append(node.getNodeName());
    
                    int prev_siblings = 1;
                    Node prev_sibling = node.getPreviousSibling();
    
                    while (null != prev_sibling) {
                        if (prev_sibling.getNodeType() == node.getNodeType()) {
                            if (prev_sibling.getNodeName().equalsIgnoreCase(node.getNodeName())) {
                                prev_siblings++;
                            }
                        }
                        prev_sibling = prev_sibling.getPreviousSibling();
                    }
                    builder.append("[").append(prev_siblings).append("]");
                } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
                    builder.append("/@");
                    builder.append(node.getNodeName());
                }
            }
    
            return builder.toString();
        }
    
        private XPath getXPath() {
            XPathFactory pathFactory = XPathFactory.newInstance();
            return pathFactory.newXPath();
        }
    

    为了 Xpath 结果更好地创建单独的 POJO 类:

    public class NodeSearchResult {
        private String xPath;
        private String value;
    
        public NodeSearchResult(String xPath, String value) {
            this.xPath = xPath;
            this.value = value;
        }
        // getters and setters
        @Override
        public String toString() {
            return String.format("%s = %s", getXPath(), getValue());
        }
    }
    

    你的 main 应该如下所示:

        List<List<NodeSearchResult>> xpathResultsList = new ArrayList<>();
        List<NodeSearchResult> searchResults;
        List<CSVDataLine> inputData = getCSVInputData();
    
        for (CSVDataLine anInputData : inputData) {
            String inputXPath = anInputData.getXPath();
            searchResults = xpathEvaluator.getXpathSearchResults(inputXPath);
            xpathResultsList.add(searchResults);
        }
    

    更新:

    我已经尝试过完成这项任务。 input.xml 是您的 xml sn-p 文件。我为您的提取创建了模拟,而不是索引的 xpath。如果您不需要保存 xpath 以供将来使用,请忽略保存到文件。只需保存到列表并稍后处理。

    这是我的主要

    public static void main(String[] args) {
        ArrayList<String> inputXpaths = new ArrayList<>();
        inputXpaths.add("/Return/ReturnData/Person/Name");
    
        XpathEvaluator evaluator = new XpathEvaluator();
        evaluator.loadFile("src/test/resources/xml/input.xml");
        List<NodeSearchResult> xpathSearchResults = evaluator.getXpathSearchResults(inputXpaths.get(0));
        System.out.println(xpathSearchResults);
    }
    

    输出:

    2015-08-25 08:07:59 [main] INFO - OPERATION - Load file for Xpath evaluation: src/test/resources/xml/input.xml
    2015-08-25 08:07:59 [main] INFO - OPERATION - Evaluate xpath expression: [/Return/ReturnData/Person/Name]
    2015-08-25 08:07:59 [main] INFO - OPERATION - Xpath evaluation results: [/Return[1]/ReturnData[1]/Person[1]/Name[1] = Yohanna, /Return[1]/ReturnData[1]/Person[2]/Name[1] = Jacoub]
    [/Return[1]/ReturnData[1]/Person[1]/Name[1] = Yohanna, /Return[1]/ReturnData[1]/Person[2]/Name[1] = Jacoub]
    

    您的问题是您没有为xpathEvaluator 加载xml 文件。它不知道从哪个文件评估 xpathes。

    【讨论】:

    • 感谢您的信息,在我研究您的代码时,您使用的是哪些导入库?
    • @mosawi 我正在使用标准的 java API。如果你已经安装了 JDK,你就不会遇到任何麻烦。 Logger 是 Log4j 的自定义实现,只需替换为 System.outStringUtils 有一种附加字符串的方法,您可以轻松地将其替换为 String.format()
    • 你的代码实现非常好,我还在经历这个。做得很好
    • 我有点困惑 List inputData = getCSVInputData();是?我拥有的数据是一个 XML 文件,我可以使用 File f = new File("path/to/xml/file.xml");但这不会是一个列表吗?我想我只是不确定如何使用你的类 XpathEvaluator
    • @mosavi 这是一个 csv 文件,带有提取的 Xpath 没有索引。您可以用ArrayList 替换它。只需将所有“未索引”的 Xpath 保存到列表 => 通过 foreach 处理此列表并通过 XpathEvaluator 评估每个未索引的 xpath => 将提取的结果收集到 NodeSearchResult 列表。
    猜你喜欢
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多