【问题标题】:How to find the node position of a value in an XML tree?如何在 XML 树中查找值的节点位置?
【发布时间】:2018-07-06 09:48:23
【问题描述】:

根据我在页面上输入的值,我想检索相关的 XML 数据。

例如,如果我从页面上的datalist 输入字段中输入/选择option“密歇根州”,我想在XML 文档中找到密歇根州的node 位置。通过知道node 的位置,我可以从与密歇根州对应的XML 文档中检索其他信息,例如GDPPOPULATION

我知道我应该将[1] 更改为[i] 等变量,但我应该如何编写函数代码?

var n = document.getElementById("myInputId");
n.addEventListener("keyup", function(event) {
  event.preventDefault();
  if (event.keyCode === 13) {
    document.getElementById("myButton").click();
  }
});

function loadXMLDoc() {
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      myFunction(xmlhttp);
    }
  };
  xmlhttp.open("GET", "state_data.xml", true);
  xmlhttp.send();
}

function myFunction(xml) {
  var x, i, xmlDoc;
  xmlDoc = xml.responseXML;
  x = xmlDoc.getElementsByTagName("UNIT")
  var s = xmlDoc.getElementsByTagName("STATE")[1].childNodes[0].nodeValue;
  document.getElementById("state").innerHTML = s;
  var gElem = xmlDoc.getElementsByTagName("GDP");
  var g = (gElem.length > 0) ? gElem[1].childNodes[0].nodeValue : "";
  document.getElementById("gdp").innerHTML = g;
  var pElem = xmlDoc.getElementsByTagName("POPULATION");
  var p = (pElem.length > 0) ? pElem[1].childNodes[0].nodeValue : "";
  document.getElementById("population").innerHTML = p;

  var a = n.value
  document.getElementById("inputValue").innerHTML = a;
  xmlDoc.UNITs.forEach(function(e, z) {
    var q = document.getElementById("inputValue").innerHTML;
    if (e.properties.id == q) {
      document.getElementById("nodePosition").innerHTML = z;
    }
  });
}
<body onload="loadXMLDoc()">
  <input list="myInput" id="myInputId" value="">
  <button id="myButton" onClick="loadXMLDoc()">submit</button>

  <p>input value: <span id="inputValue"></span></p>
  <p>XML tree node position of input value: <span id="nodePosition"></span></p>
  <p>State: <span id="state"></span></p>
  <p>GDP: <span id="gdp"></span></p>
  <p>Population: <span id="population"></span></p>

  <datalist id="myInput">
<option id="AL">Alabama</option>
<option id="CA">California</option>
<option id="MI">Michigan</option>
<option id="TX">Texas</option>
<option id="WI">Wisconsin</option>
</datalist>

这是我的 XML:

<?xml version="1.0" encoding="UTF-8"?>
<STATE_DATA>
 <UNIT>
    <STATE>Wisconsin</STATE>
    <GDP>232,300,000,000</GDP>
    <POPULATION>5,800,000</POPULATION>
 </UNIT>
 <UNIT>
    <STATE>Alabama</STATE>
    <GDP>165,800,000,000</GDP>
    <POPULATION>4,900,000</POPULATION>
 </UNIT>
 <UNIT>
    <STATE>California</STATE>   
    <!-- Note: the GDP node for this unit is missing -->
    <POPULATION>39,600,000</POPULATION>
 </UNIT>
 <UNIT>
    <STATE>Texas</STATE>
    <GDP>1,600,000,000,000</GDP>
    <POPULATION>28,300,000</POPULATION>
 </UNIT>
 <UNIT>
    <STATE>Michigan</STATE>
    <GDP>382,000,000</GDP>
    <POPULATION>10,000,000</POPULATION>
 </UNIT>
</STATE_DATA>

【问题讨论】:

    标签: javascript xmlhttprequest


    【解决方案1】:

    如果您使用现代浏览器附带的 xml 处理功能,事情会有所简化。 重写以 XPath 表达式为中心。完整的介绍超出了此答案的范围,但简而言之,它是一种从 xml 树中检索内容的模式语言。

    xpath 表达式用于从 xml 文件中提取相关元素。

    该解决方案不需要对 xml 进行任何事先修改(与该解决方案的先前修订相反)。

    代码示例是独立的 - 只需将其保存为本地 html 文件或从 http 服务器提供。为了使其工作,xml 数据存储在一个脚本元素中,作为数据岛。

    将设计改回使用 ajax 调用获取 xml 数据应该很简单。

    将代码视为展示基于 xpath 的方法结构的概念证明。

    <!DOCTYPE html>
    <!--
        SO
        datalist / xml handling
        Q 51200490 (https://stackoverflow.com/questions/51200490/how-to-find-the-node-position-of-a-value-in-an-xml-tree/51201494)
        A 
    -->
    <html>
        <head>
            <title>SO sample</title>
            <script>
     // Setup of keypress event handler, default selection of xml data.
     function setupEH () {
        var n = document.getElementById("myInputId");
        n.addEventListener("keyup", function(event) {
            event.preventDefault();
            if (event.keyCode === 13) {
                document.getElementById("myButton").click();
            }
        });
    
        loadXMLDoc('Alabama'); // comment out this line if you want a vanilla UI after loading the html page.
    }
    
    // Load the xml document
    function loadXMLDoc( statelabel ) {
        // The xml document is retrieved with the following steps:
        //      1. Obtain the (in-document) source as a DOM node.
        //      2. Extract textual content.
        //      3. Instantiate the xml parser (a browser built-in)
        //      4. Parse textual content into an xml document
        //
        //  When retrieving the xml document by means of ajax, these steps will be handled by the library for you - a parsed xml document will be available as a property or through calling a method.
        //
        let x_xmlisland = document.getElementById("template_xml");
        let s_xmlsource = x_xmlisland.textContent; 
        let parser = new DOMParser();
        let xmlDoc = parser.parseFromString(s_xmlsource, "application/xml");
    
        myFunction(xmlDoc, statelabel); // Actual work ...
    }
    
    // Processing the xml document 
    function myFunction(xmlDoc, statelabel) {
        //    debugger; // uncomment to trace
    
        //
        //  Every bit of information is processed as follows:
        //      - Get the relevant xml subtree ( `UNIT` element of the selected state incl.descendants )
        //      - Extract the textual value.
        //      - Feed the textual value to the Html elements prsenting the result.
        //
        var xpr_current_unit  = xmlDoc.evaluate("/STATE_DATA/UNIT[./STATE[./text() = '"+statelabel+"']]",xmlDoc,null,XPathResult.ORDERED_NODE_ITERATOR_TYPE,null);
        var node_current_unit = xpr_current_unit.iterateNext();
    
        //
        //  The subsequent calls to xmlDoc.evaluate set the current UNIT element as their context node ('starting point'/'temporary root' for the xpath expression).
        //  The context node is referenced by '.' (dot) 
        //
        var xpr_s   = xmlDoc.evaluate("./STATE/text()",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
        var node_s  = xpr_s.iterateNext();
        var s       = node_s.textContent
        document.getElementById("state").innerHTML = s;
    
        var xpr_g   = xmlDoc.evaluate("./GDP/text()",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
        var node_g  = xpr_g.iterateNext();
        var g = "Unknown";
        if ( node_g !== null ) {
            g = node_g.textContent;
        }
        document.getElementById("gdp").innerHTML = g;
    
        var xpr_p   = xmlDoc.evaluate("./POPULATION/text()",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
        var node_p  = xpr_p.iterateNext();
        var p = "Unknown";
        if ( node_p !== null ) {
            p = node_p.textContent;
        }
        document.getElementById("population").innerHTML = p;
    
        // cf. https://stackoverflow.com/a/3437009
        var xpr_u  = xmlDoc.evaluate("count(./preceding::UNIT)+1.",node_current_unit,null,XPathResult.ORDERED_ANY_TYPE,null);
        var n_ucount = xpr_u.numberValue;
    
        document.getElementById("inputValue").innerHTML = s;
        document.getElementById("nodePosition").innerHTML = n_ucount;
    }
    
    // Setup the submit click handler
    function ehClick ( ) {
        let node_choice     = document.getElementById('myInputId');
        loadXMLDoc(node_choice.value);
    }
            </script>
            <style>
            </style>
        </head>
        <body onload="setupEH()">
            <script id="template_xml" type="text/xml"><?xml version="1.0" encoding="UTF-8"?>
    <STATE_DATA>
     <UNIT>
        <STATE>Wisconsin</STATE>
        <GDP>232,300,000,000</GDP>
        <POPULATION>5,800,000</POPULATION>
     </UNIT>
     <UNIT>
        <STATE>Alabama</STATE>
        <GDP>165,800,000,000</GDP>
        <POPULATION>4,900,000</POPULATION>
     </UNIT>
     <UNIT>
        <STATE>California</STATE>   
        <!-- Note: the GDP node for this unit is missing -->
        <POPULATION>39,600,000</POPULATION>
     </UNIT>
     <UNIT>
        <STATE>Texas</STATE>
        <GDP>1,600,000,000,000</GDP>
        <POPULATION>28,300,000</POPULATION>
     </UNIT>
     <UNIT>
        <STATE>Michigan</STATE>
        <GDP>382,000,000</GDP>
        <POPULATION>10,000,000</POPULATION>
     </UNIT>
    </STATE_DATA>
            </script>
            <input list="myInput" id="myInputId" value="">
            <button id="myButton" onClick="ehClick()">submit</button>
    
            <p>input value: <span id="inputValue"></span></p>
            <p>XML tree node position of input value: <span id="nodePosition"></span></p>
            <p>State: <span id="state"></span></p>
            <p>GDP: <span id="gdp"></span></p>
            <p>Population: <span id="population"></span></p>
    
            <datalist id="myInput">
            <option id="AL">Alabama</option>
            <option id="CA">California</option>
            <option id="MI">Michigan</option>
            <option id="TX">Texas</option>
            <option id="WI">Wisconsin</option>
            </datalist>
        </body>
    </html>
    

    参考文献

    【讨论】:

    • 感谢您提供详尽的回答,尽管有一个警告我应该清楚。 XML 文件位于外部服务器上。使用 XPath,是否仍然可以修改 XML?
    • @logoologist XPath 只是一种模式语言。要处理 XML 文档,您需要使用 DOM 方法或 xslt(另一种 xml 技术,基本上是对 xml 树的抽象进行函数式编程)。但是,可以调整解决方案以避免对 xml 进行任何更改。我会相应地补充答案。
    • 您会在解决方案中进行哪些调整以避免更改 XML?
    • 我想知道是否可以使用这样的东西来完全索引 XML:xmlDoc.forEach(function(e, z){
    • @logoologist 我现在正在处理它 :) 基本上 Xpath 提供了引用元素文本内容的方法(在 xml 树的抽象模型中,这对应于它们自己的节点)。对于您的索引想法,您输入 xslt 领域。这绝对值得学习,但对于手头的任务可能有点过头了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多