围绕XML包装和删除CDATA

时间:2016-08-19 01:58:53

标签: sql sql-server xml cdata

这是我的xml。我的目标是在导出时使用CDATA将数据包装在Value节点内,然后将其导入Xml类型列并删除CDATA。

<Custom>
     <Table>Shape</Table>
     <Column>CustomScreen</Column>
     <Value>Data</Value>
<Custom>

现在我用表格中的XML替换Value节点内的'Data',然后我相信我将CData放在它周围,其中ShapeInfo是XML类型,CustomPanel是[ShapeInfo] XML的第一个节点。

SET @OutputXML= replace(@OutputXML, 'Data', CAST((SELECT [ShapeInfo]      
                         FROM [Shape] WHERE [Shape_ID] = @ShapeID) as VARCHAR(MAX))

SET @OutputXML= replace(@OutputXML, '<CustomPanel', '<![CDATA[<CustomPanel')

然而,结果看起来像这样,即使我预计它只有CDATA围绕信息:

<Value>&lt;CustomPanel VisibilityIndicator=""&gt;&lText="No" Checked="False" Height="20" Width="50"/&gt;&lt;/Cell&gt;&lt;/Row&gt;&lt;/Table&gt;&lt;/CustomPanel&gt;</Value>

然后我正在做一些动态的sql来更新那个列

EXEC('UPDATE ['+ @tableName +  '] SET [' + @columnName + '] = ''' + @nodeValue + ''' WHERE Shape_ID = ''' + @ShapeID + '''')

我被告知我可以使用以下内容删除CDATA,但我没有使用它。

declare @x xml
set @x=N'<Value>&lt;CustomPanel....... all the current info ...=&quot;&quot;&gt;</Value>'

select @x.value('(/Value)[1]', 'nvarchar(max)')

select '<![CDATA[' + @x.value('(/Value)[1]', 'nvarchar(max)') + ']]'

再次检查列后,它似乎包含正确的信息。但是我从未将它从VARCHAR更改回XML或删除了CDATA符号,即使它们在检查列时似乎已经消失了。那我在这里错过了什么?这是一个正确的方法吗?

2 个答案:

答案 0 :(得分:2)

如果您需要完全控制生成XML,可以使用FOR XML EXPLICIT

DECLARE @xml xml = '<Custom>
     <Table>Shape</Table>
     <Column>CustomScreen</Column>
     <Value>Data</Value>
</Custom>';

WITH rawValues AS
(
    SELECT
        n.value('Table[1]', 'nvarchar(20)') [Table],
        n.value('Column[1]', 'nvarchar(20)') [Column],
        n.value('Value[1]', 'nvarchar(20)') [Value]
    FROM @xml.nodes('Custom') X(n)
)
SELECT 1 AS Tag,
       NULL AS Parent,
       [Table] AS [Custom!1!Table!ELEMENT],
       [Column] AS [Custom!1!Column!ELEMENT],
       [Value] AS [Custom!1!Value!CDATA]
FROM rawValues 
FOR XML EXPLICIT

它生成:

<Custom>
  <Table>Shape</Table>
  <Column>CustomScreen</Column>
  <Value><![CDATA[Data]]></Value>
</Custom>

如果您需要反向,请替换源XML并使用ELEMENT代替CDATA

答案 1 :(得分:1)

如果您确实需要XML中的CDATA部分,则只有两个选项

  • 字符串连接(非常糟糕)
  • FOR XML EXPLICIT(在这种情况下,你得到了Pawel的回答)

但是你应该考虑到CDATA部分仅存在延迟输入。 完全没有区别是否将内容括在CDATA部分或正确转义。因此,Microsoft决定甚至不支持现代XML方法中的CDATA语法。它只是不需要。

看看这些例子:

- 我从包含相同内容的字符串开始,并在CDATA

DECLARE @s VARCHAR(500)=
'<root>
<a>Normal Text</a>
<a>Text with forbidden character &amp; &lt;&gt;</a>
<a><![CDATA[Text with forbidden character & <>]]></a>
</root>';

- 此字符串已转换为XML。

DECLARE @x XML=CAST(@s AS XML);

- 这是输出,您可以看到,CDATA部分已被编码为否CDATA了。 CDATA将始终由有效的转义字符串替换:

SELECT @x;

<root>
  <a>Normal Text</a>
  <a>Text with forbidden character &amp; &lt;&gt;</a>
  <a>Text with forbidden character &amp; &lt;&gt;</a>
</root>

- 反投影显示,XML内部不再有CDATA

SELECT CAST(@x AS VARCHAR(500));

<root>
   <a>Normal Text</a>
   <a>Text with forbidden character &amp; &lt;&gt;</a>
   <a>Text with forbidden character &amp; &lt;&gt;</a>
</root>

- 逐个读取节点显示正确的内容

SELECT a.value('.','varchar(max)')
FROM @x.nodes('/root/a') AS A(a)

Normal Text
Text with forbidden character & <>
Text with forbidden character & <>

使用CDATA并坚持认为必须将其包含在XML的文本表示(不是XML!)中的唯一原因是第三方或遗留要求。< / p>

请记住:如果使用字符串连接,则只能以字符串格式存储带有可读CDATA的XML。每当您将其转换为XML时,CDATA将被省略。使用FOR XML EXPLICIT允许类型安全存储,但是使用更深的嵌套非常笨拙。使用外部接口可能没问题,但你应该三思而后行......

相关答案的两个链接(由我:-)):