使用自关闭节点替换xml中的已配对空节点

时间:2017-06-21 11:36:22

标签: sql-server xml sql-server-2008 xslt ssis-2008

我有一些xml,其中一部分看起来像这样:

<BasicInfo>
  <Foo>80</Foo>
  <Bar>
  </Bar>
</BasicInfo>

我想用单个自闭节点替换所有空节点开关对(如上面的<Bar>),得到这样的结果:

<BasicInfo>
  <Foo>80</Foo>
  <Bar />
</BasicInfo>

我知道在xml中,两者是等价的,xslt会选择输出空节点的方式等等,但我有很多空节点,两个解释中预期的文件大小差异很大足以值得担心;我希望尽可能有效地存储xml。

上面的xml是从SQL Server创建的,然后用另一个XSLT转换,删除了SQL Server 2008留下的所有“xsi:nil”值和xmlns声明:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />
  <xsl:strip-space elements="*" />
  <xsl:template match="*">
    <xsl:element name="{local-name(.)}">
      <xsl:apply-templates select="@* | node()" />
    </xsl:element>
  </xsl:template>
  <xsl:template match="@*">
    <xsl:attribute name="{local-name(.)}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>
  <xsl:template match="@*[local-name(.)='noNamespaceSchemaLocation']" />
  <xsl:template xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" match="@xsi:nil" />
</xsl:stylesheet>

正是这个样式表将开 - 关对留下 - 有没有办法强制它留下自闭节点,或者让新的xslt自己产生这种效果?

遗憾的是,删除节点不是一个选择:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" version="1.0">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />
  <xsl:strip-space elements="*" />
  <!--Remove all the empty nodes-->
  <!--copy nodes-->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>
  <!--match only those with no contents at all-->
  <xsl:template match="*[not(@*|*|comment()|processing-instruction()) and normalize-space()='']" />
  <!--now check all those that have existing but empty children and don't return the children if they are empty-->
  <xsl:template match="*">
    <xsl:copy>
      <xsl:if test="descendant::text()">
        <xsl:apply-templates select="node()" />
      </xsl:if>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

但这是我最接近的。我发现其他人都在问这个问题,但是我对xslt的了解很少,这意味着我无法改变满足我需求的解决方案(需要很长时间才能解决上面的问题!)。看起来像标准化空间这样的东西是要走的路?我真的很感激一些帮助!

NB。 xml由SQL Server 2008 R2使用FOR XML PATH生成,XSLT脚本存储在服务器上的xml列中; SSIS 2008中的操作数类型为XSLT的XML任务应用转换。

2 个答案:

答案 0 :(得分:1)

假设:

<强> XML

<BasicInfo>
  <Foo>80</Foo>
  <Bar>
  </Bar>
</BasicInfo>

以下样式表:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

</xsl:stylesheet>

将返回:

<?xml version="1.0" encoding="UTF-8"?>
<BasicInfo>
  <Foo>80</Foo>
  <Bar/>
</BasicInfo>

<强>解释

<xsl:strip-space elements="*"/>指令将删除Bar元素包含的仅空白文本节点,处理器将自动输出一个空元素作为自关闭标记。

答案 1 :(得分:1)

NULL的正常行为是完全省略该元素......

如果我做对了,你就ELEMENTS XSINIL强制引擎引入所有元素,即使是NULL

一个hacky技巧可能是删除像这里的属性:

CREATE TABLE #Demo ( Id INT IDENTITY(1,1)   NOT NULL 
                    ,Value1 VARCHAR(30) NOT NULL 
                    ,Value2 VARCHAR(30) NULL ); 
INSERT #Demo(Value1,Value2) 
    VALUES('Bar',NULL); 


WITH XMLNAMESPACES ('http://tempuri.org/MySchema.xsd' AS xsd) 
SELECT This.Id 
     ,( SELECT T.Value1 
              ,T.Value2
        FROM    #Demo   T 
        WHERE   T.Id = This.Id 
        FOR XML PATH('BasicInfo'),ELEMENTS XSINIL,TYPE) AS TheXml
INTO #Demo2
FROM    #Demo   AS This;
UPDATE #Demo2 SET TheXml.modify('delete (//*/@*[local-name()="nil"])');

SELECT * FROM #Demo2 
GO
DROP TABLE #Demo
DROP TABLE #Demo2;