首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有名称空间的XML到JSON

带有名称空间的XML到JSON
EN

Stack Overflow用户
提问于 2015-12-03 16:47:34
回答 2查看 8.3K关注 0票数 1

我试图使用XSLT将XML命名空间XML转换为JSON数据,如下所示:

基本上,如果XML元素有一个表单元素的节点,xmnls=“urn:ietf:params:xml:ns:yang:ietf-xmnls=”。JSON的转换应该是"ietf-modulename:element“,如下例所示

XML:

代码语言:javascript
复制
<notification
    xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
    <event-time>2013-12-21T00:01:00Z</event-time>
    <event>
    <event-class>fault</event-class>
    <reporting-entity>
    <card>Ethernet0</card>
    </reporting-entity>
    <severity>major</severity>
    </event>
 </notification>

JSON Conversion:

{
"ietf-restconf:notification": {
"event-time": "2013-12-21T00:01:00Z",
"event": {
"event-class": "fault",
"reporting-entity": { "card": "Ethernet0" },
"severity": "major"
}

使用以下xslt:

代码语言:javascript
复制
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="/">{
  <xsl:apply-templates select="*"/>}
  </xsl:template>

  <!-- Object or Element Property-->
  <xsl:template match="*">
  "<xsl:value-of select="name()"/>" : <xsl:call-template name="Properties"/>
                                       </xsl:template>

                                       <!-- Array Element -->
                                       <xsl:template match="*" mode="ArrayElement">
                                       <xsl:call-template name="Properties"/>
                                       </xsl:template>

                                       <!-- Object Properties -->
                                       <xsl:template name="Properties">
                                       <xsl:variable name="childName" select="name(*[1])"/>
                                       <xsl:choose>
                                       <xsl:when test="not(*|@*)">"<xsl:value-of select="."/>"</xsl:when>
                                       <xsl:when test="count(*[name()=$childName]) > 1">{ "<xsl:value-of select="$childName"/>" :[<xsl:apply-templates select="*" mode="ArrayElement"/>] }</xsl:when>
                                       <xsl:otherwise>{
                                         <xsl:apply-templates select="@*"/>
                                           <xsl:apply-templates select="*"/>
                                       }</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::*">,</xsl:if>
</xsl:template>

<!-- Attribute Property -->
<xsl:template match="@*">"<xsl:value-of select="name()"/>" : "<xsl:value-of select="."/>",
  </xsl:template>
    </xsl:stylesheet>

但是名称空间在这里被删除了。如何编辑XSL文件以在JSON中转换为"ietf-restconf:notification“?

另一个例子是:

代码语言:javascript
复制
<interfaces
xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>0/4</name>
<type
xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd
</type>
<enabled>false</enabled>
<link-up-down-trap-enable>enabled</link-up-down-trap-enable>
<ipv4
xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>false</enabled>
</ipv4>
<ipv6
xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<enabled>false</enabled>
<mtu>1500</mtu>
<forwarding>false</forwarding>
<dup-addr-detect-transmits>1</dup-addr-detect-transmits>
<autoconf>
<create-global-addresses>false</create-global-addresses>
</autoconf>
</ipv6>
</interface>
</interfaces> 

应该映射到

代码语言:javascript
复制
    {"ietf-interfaces:interfaces": {
  "interface": {
    "name": "0/4",
    "iana-if-type:type": "ianaift:ethernetCsmacd
",
    "enabled": "false",
    "link-up-down-trap-enable": "enabled",
    "ietf-ip:ipv4": {
      "enabled": "false"
    },
    "ietf-ip:ipv6": {
      "enabled": "false",
      "mtu": "1500",
      "forwarding": "false",
      "dup-addr-detect-transmits": "1",
      "autoconf": {
        "create-global-addresses": "false"
      }
    }
  }
}}
EN

回答 2

Stack Overflow用户

发布于 2015-12-04 07:37:14

您的要求中最困难的部分是前缀。

当你说(略为提高可读性)时:

基本上,如果XML元素有一个表单元素xmnls="urn:ietf:params:xml:ns:yang:ietf-modulename"的节点,那么JSON转换应该是"ietf-modulename: element“,如下例所示

您似乎在谈论具有xmlns=""命名空间声明的元素。这不是XSLT处理器在XML中看到的属性。它是绑定到元素的命名空间(并且是元素的后代,除非他们指定了不同的名称空间)。

所以,与notification元素不同,它实际上是

代码语言:javascript
复制
{urn:ietf:params:xml:ns:yang:ietf-restconf}notification

元素。

在您的示例中,所有子代元素也位于同一个命名空间中。

event-time为例:

代码语言:javascript
复制
{urn:ietf:params:xml:ns:yang:ietf-restconf}event-time

下面是一个很好的名称空间引用,以获得更好的解释:http://www.jclark.com/xml/xmlns.htm

我认为您唯一能做的就是检查根元素上的命名空间uri,然后检查默认名称空间是否已经更改。

看一下您的示例,您似乎希望在上一个:之后获得uri的一部分。您可以通过使用递归模板调用来实现这一点,但是在XSLT2.0中会容易得多。

下面是一个例子。我是为了可读性而缩进的,但没有必要。只需删除名为indentindent变量的模板及其引用即可。(在XSLT2.0中,这也要容易得多。)

它并不全面,但适用于您的示例,应该给您一个起点。

在这里也可以看到示例:http://xsltransform.net/6r5Gh3i (需要在服务可用时进行更新)

XML输入

代码语言:javascript
复制
<interfaces
    xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
    <interface attr="x">
        <name>0/4</name>
        <type
            xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd
        </type>
        <enabled>false</enabled>
        <link-up-down-trap-enable>enabled</link-up-down-trap-enable>
        <ipv4
            xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
            <enabled>false</enabled>
        </ipv4>
        <ipv6
            xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
            <enabled>false</enabled>
            <mtu>1500</mtu>
            <forwarding>false</forwarding>
            <dup-addr-detect-transmits>1</dup-addr-detect-transmits>
            <autoconf>
                <create-global-addresses>false</create-global-addresses>
            </autoconf>
        </ipv6>
    </interface>
</interfaces>

XSLT1.0

代码语言:javascript
复制
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/*" priority="1">
    <xsl:variable name="prefix">
      <xsl:call-template name="getPrefix">
        <xsl:with-param name="uri" select="namespace-uri()"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="concat('{&quot;',$prefix,local-name(),'&quot;')"/>
    <xsl:text>: {&#xA;</xsl:text>
    <xsl:apply-templates/>
    <xsl:text>}}</xsl:text>
  </xsl:template>

  <xsl:template match="*[not(*)]">
    <xsl:variable name="indent">
      <xsl:call-template name="indent">
        <xsl:with-param name="level" select="count(ancestor::*)"/>
      </xsl:call-template>      
    </xsl:variable>
    <xsl:variable name="prefix">
      <xsl:if test="namespace-uri()!=namespace-uri(..)">
        <xsl:call-template name="getPrefix">
          <xsl:with-param name="uri" select="namespace-uri()"/>
        </xsl:call-template>        
      </xsl:if>
    </xsl:variable>
    <xsl:value-of select="concat($indent,'&quot;',$prefix,local-name(),'&quot;: ')"/>
    <xsl:apply-templates/>
    <xsl:if test="following-sibling::*">,</xsl:if>
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>

  <xsl:template match="*[*]">
    <xsl:variable name="indent">
      <xsl:call-template name="indent">
        <xsl:with-param name="level" select="count(ancestor::*)"/>
      </xsl:call-template>      
    </xsl:variable>
    <xsl:variable name="prefix">
      <xsl:if test="namespace-uri()!=namespace-uri(..)">
        <xsl:call-template name="getPrefix">
          <xsl:with-param name="uri" select="namespace-uri()"/>
        </xsl:call-template>        
      </xsl:if>
    </xsl:variable>
    <xsl:value-of select="concat($indent,'&quot;',$prefix,local-name(),'&quot;')"/>
    <xsl:text>: {&#xA;</xsl:text>
    <xsl:apply-templates/>
    <xsl:value-of select="concat($indent,'}')"/>
    <xsl:if test="following-sibling::*">,</xsl:if>
    <xsl:text>&#xA;</xsl:text>    
  </xsl:template>

  <xsl:template match="text()">
    <xsl:value-of select="concat('&quot;',normalize-space(),'&quot;')"/>
  </xsl:template>

  <xsl:template name="indent">
    <xsl:param name="level"/>
    <xsl:if test="$level >= 1">
      <xsl:text>  </xsl:text>
    </xsl:if>
    <xsl:if test="$level - 1 >= 1">
      <xsl:call-template name="indent">
        <xsl:with-param name="level" select="$level - 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template name="getPrefix">
    <xsl:param name="uri"/>
    <xsl:if test="namespace-uri()">
      <xsl:choose>
        <xsl:when test="contains($uri,':')">
          <xsl:call-template name="getPrefix">
            <xsl:with-param name="uri" select="substring-after($uri,':')"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$uri"/>
          <xsl:if test="$uri">:</xsl:if>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

输出

代码语言:javascript
复制
{"ietf-interfaces:interfaces": {
  "interface": {
    "name": "0/4",
    "type": "ianaift:ethernetCsmacd",
    "enabled": "false",
    "link-up-down-trap-enable": "enabled",
    "ietf-ip:ipv4": {
      "enabled": "false"
    },
    "ietf-ip:ipv6": {
      "enabled": "false",
      "mtu": "1500",
      "forwarding": "false",
      "dup-addr-detect-transmits": "1",
      "autoconf": {
        "create-global-addresses": "false"
      }
    }
  }
}}

这个输出和您请求的输出之间唯一的区别是使用type元素。迈克尔在回答中最好地解释了这一点:

注意,在您的(新)示例中,type元素与其父节点位于同一个名称空间中。命名空间声明xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type"不影响它。实际上,它不影响整个XML中的任何节点。

票数 1
EN

Stack Overflow用户

发布于 2015-12-04 11:31:58

在输出元素的名称之前,尝试添加以下内容:

代码语言:javascript
复制
<xsl:if test="namespace-uri()!=namespace-uri(..)">
    <xsl:value-of select="concat(substring-after(namespace-uri(), 'urn:ietf:params:xml:ns:yang:'), ':')"/>
</xsl:if>

这部分是基于您链接到的标准的以下语句:

必须为顶级JSON对象的所有成员使用命名空间限定成员名称,然后每当节点及其父节点的命名空间不同时,也必须使用命名空间限定成员名。

部分原因在于你的声明:

我的要求是,应该提取"urn:ietf:params:xml:ns:yang:“之后的所有内容,并将其作为前缀添加到任何具有命名空间的元素中。

请注意,在您的(新)示例中,type元素与其父节点位于同一个名称空间中的。命名空间声明xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type"不影响它。实际上,它不影响整个XML中的任何节点。

与您的问题无关:我建议您使用xsl:text提供JSON标记和缩进。这将使样式表更加可读性更强,更易于修改。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34071614

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档