【问题标题】:Wrapping and removing CDATA around XML围绕 XML 包装和删除 CDATA
【发布时间】:2016-12-26 01:57:59
【问题描述】:

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

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

现在我正在用表中的 XML 替换 Value 节点内的“数据”,然后我相信我将 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 符号,即使当我检查该列时它们似乎已消失。那么我在这里错过了什么?这是正确的做法吗?

【问题讨论】:

  • 顺便说一句:这一行 SET @OutputXML= replace(@OutputXML, '&lt;CustomPanel', '&lt;![CDATA[&lt;CustomPanel') 不会添加结束 ]]&gt;。

标签: sql sql-server xml cdata


【解决方案1】:

如果您需要完全控制生成 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,但是 - AFAIC - 关于将 CDATA 摆弄到 XML 中应该三思而后行。在大多数情况下,它只是不需要...
  • 尽管我尝试按照您写的方式进行连接,但我最终还是进行了字符串连接。
【解决方案2】:

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

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

但您应该考虑到,CDATA 部分仅用于延迟输入。内容是包含为CDATA 部分还是正确转义,绝对没有区别。因此微软决定在现代 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!)中的唯一原因是第三方或遗留要求。

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

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

【讨论】:

  • @Schnugo 一如既往的好信息。我最终做了字符串连接。但我有个问题。 CData 在可能发生“sql 注入”的情况下是否有用?有人告诉我这将有助于防止人们插入随机的东西。
猜你喜欢
  • 2014-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多