【问题标题】:GET Request XML response parsing using XPath to produce a JS Object使用 XPath 解析 GET 请求 XML 响应以生成 JS 对象
【发布时间】:2021-05-13 20:31:21
【问题描述】:

问题是我从一个 GET 请求到 WMS 服务器得到一个“application/xml”响应,然后我使用 DOMParser().parseFromString() 方法使用 XPath 表达式进行解析。我尝试查看不同的解决方案,例如专用库,但我发现了有关 XPath 表达式的信息,现在我需要帮助编写一个 XPath 表达式,它将返回层标签的名称和 XML 响应的标题,看起来像

<Layer>
<Title>Title Level 1</Title>
<Name>Name Level 1</Name>
    <Layer>
    <Title>Title Level 2</Title>
    <Name>Name Level 2</Name>
        <Layer>
        <Title>Title Level 3-1</Title>
        <Name>Name Level 3-1</Name>
        </Layer>
        <Layer>
        <Title>Title Level 3-2</Title>
        <Name>Name Level 3-2</Name>
        </Layer>
    </Layer>
</Layer>

并且会返回一个 JSON

{
title: 'Title Level 1'
name: 'Name Level 1'
children: [
    {
     title: 'Title Level 2'
     name: 'Name Level 2'
     children: [
         {
          title: 'Title Level 3-1'
          name: 'Name Level 3-1'
         },
         {
          title: 'Title Level 3-1'
          name: 'Name Level 3-1'
         }
     ]
]
}

【问题讨论】:

  • XPath 用于查询 XML。与其说是构建输出流。你可以看看 xslt。 Is 使用 XPath 来匹配 XML 节点并且可以输出任何你喜欢的东西。这个答案stackoverflow.com/a/24123408/3710053可以给你一个好主意

标签: javascript xml xpath tree wms


【解决方案1】:

您可以在浏览器中使用 XSLT 3 和 Saxon-JS 2 (https://www.saxonica.com/download/javascript.xml) 将 XML 转换为 JSON:

const xml = `<Layer>
<Title>Title Level 1</Title>
<Name>Name Level 1</Name>
    <Layer>
    <Title>Title Level 2</Title>
    <Name>Name Level 2</Name>
        <Layer>
        <Title>Title Level 3-1</Title>
        <Name>Name Level 3-1</Name>
        </Layer>
        <Layer>
        <Title>Title Level 3-2</Title>
        <Name>Name Level 3-2</Name>
        </Layer>
    </Layer>
</Layer>`;

const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    xmlns="http://www.w3.org/2005/xpath-functions"
    expand-text="yes"
    version="3.0">

  <xsl:output method="json" build-tree="no"/>
  
  <xsl:template match="/*" priority="5">
    <xsl:variable name="json-xml">
      <map>
        <xsl:apply-templates/>
      </map>
    </xsl:variable>
    <xsl:sequence select="xml-to-json($json-xml) => parse-json()"/>
  </xsl:template>
  
  <xsl:template match="*[not(*)]">
    <string key="{local-name()}">{.}</string>
  </xsl:template>
  
  <xsl:template match="Layer[1]">
    <array key="children">
       <xsl:apply-templates select="../Layer" mode="map"/>
    </array>
  </xsl:template>
  
  <xsl:template match="Layer[position() > 1]"/>
  
  <xsl:template match="Layer" mode="map">
    <map>
      <xsl:apply-templates/>
    </map>
  </xsl:template>
  
</xsl:stylesheet>`;

const jsonResult = SaxonJS.XPath.evaluate(`transform(map { 'source-node' : parse-xml($xml), 'stylesheet-text' : $xslt, 'delivery-format' : 'raw' })?output`, [], { 'params' : { 'xml' : xml, 'xslt' : xslt } });

console.log(jsonResult);
&lt;script src="https://xsltfiddle-beta.liberty-development.net/js/SaxonJS2/SaxonJS2.js"&gt;&lt;/script&gt;

一种稍有不同但可能更紧凑的 XSLT 3 方法是将 XML 直接转换为映射和数组输出为 JSON 对象和数组:

const xml = `<Layer>
<Title>Title Level 1</Title>
<Name>Name Level 1</Name>
    <Layer>
    <Title>Title Level 2</Title>
    <Name>Name Level 2</Name>
        <Layer>
        <Title>Title Level 3-1</Title>
        <Name>Name Level 3-1</Name>
        </Layer>
        <Layer>
        <Title>Title Level 3-2</Title>
        <Name>Name Level 3-2</Name>
        </Layer>
    </Layer>
</Layer>`;

const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
   exclude-result-prefixes="#all"
   xmlns="http://www.w3.org/2005/xpath-functions"
   xmlns:mf="http://example.com/mf"
   expand-text="yes"
   version="3.0">
  
  <xsl:strip-space elements="*"/>

  <xsl:output method="json" build-tree="no" indent="yes"/>
  
  <xsl:template match="/Layer" priority="5">
<xsl:map>
  <xsl:apply-templates/>
</xsl:map>
  </xsl:template>
  
  <xsl:template match="*[not(*)]">
<xsl:map-entry key="local-name()" select="data()"/>
  </xsl:template>
  
  <xsl:template match="Layer[1]">
<xsl:map-entry key="'children'">
  <xsl:sequence select="array { ../Layer/mf:apply-templates(.) }"/>
</xsl:map-entry>
  </xsl:template>
  
  <xsl:template match="Layer[position() > 1]"/>
  
  <xsl:function name="mf:apply-templates" as="item()*">
<xsl:param name="elements" as="element(*)*"/>
<xsl:map>
  <xsl:apply-templates select="$elements/*"/>      
</xsl:map>
  </xsl:function>
  
</xsl:stylesheet>`;

const jsonResult = SaxonJS.XPath.evaluate(`
  transform(
    map { 
      'source-node' : parse-xml($xml), 
      'stylesheet-text' : $xslt, 
      'delivery-format' : 'raw' 
    }
  )?output`, 
  [], 
  { 'params' : { 'xml' : xml, 'xslt' : xslt } }
);

console.log(jsonResult);
&lt;script src="https://xsltfiddle-beta.liberty-development.net/js/SaxonJS2/SaxonJS2.js"&gt;&lt;/script&gt;

在两个例子中,为了紧凑(以及一个可执行的单个 StackOverflow 代码 sn-p 的优雅),我使用 XPath 3.1 transform 函数直接从 JavaScript 运行 XSLT 3 代码;虽然 Saxon-JS 2 在浏览器和 Node.js 中都完美地支持了这一点,但应该注意的是,出于性能原因,如果您开发并修复了 XSLT,推荐的方法是使用 Saxon EE 或使用Saxon JS 2 的 xslt3 命令行从 XSLT 到 SEF(即从基于 XML 的 XSLT 代码到基于 JSON 的 SEF 格式),以便可以将 SEF 直接馈送到 Saxon JS API https://www.saxonica.com/saxon-js/documentation/index.html#!api/transform 中的 transform 函数。这在现实世界的场景中应该比使用 SaxonJS.XPath.evaluate 表现得更好,并且在 Node.js 上还允许您使用异步编程。

【讨论】:

  • 非常感谢 Honnen 先生。唯一的问题是,为什么生成的 JSON 对根的孩子有单独的 Title 和 Name 对象,而不是它如何处理根?
  • @YordanRadev,是的,你是对的,我没有注意到第二个样本创建的内部格式不是你需要的。我已经更正了上面第二个代码示例的 XSLT 代码以构造正确的格式(我希望如此)。
  • 再次感谢您,先生,但是 XSLT 模板返回一个包含所有内容的大型对象,如果可以请向我解释如何仅递归获取层的标题和名称以及其子级的数组 @ 987654323@
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-24
  • 2019-08-10
  • 1970-01-01
  • 2013-11-23
  • 1970-01-01
  • 2021-03-15
  • 2012-03-09
相关资源
最近更新 更多