在 XML 周围包装和删除 CDATA
Wrapping and removing CDATA around XML
这是我的xml。我的目标是在导出时使用 CDATA 将数据包装在 Value 节点内,然后将其导入回 Xml 类型的列并移除 CDATA。
<Custom>
<Table>Shape</Table>
<Column>CustomScreen</Column>
<Value>Data</Value>
<Custom>
现在我正在用 table 中的 XML 替换值节点内的 '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><CustomPanel VisibilityIndicator="">&lText="No" Checked="False" Height="20" Width="50"/></Cell></Row></Table></CustomPanel></Value>
然后我正在做一些动态 sql 来更新该列
EXEC('UPDATE ['+ @tableName + '] SET [' + @columnName + '] = ''' + @nodeValue + ''' WHERE Shape_ID = ''' + @ShapeID + '''')
有人告诉我可以使用以下方法删除 CDATA,但我没有使用它。
declare @x xml
set @x=N'<Value><CustomPanel....... all the current info ...=""></Value>'
select @x.value('(/Value)[1]', 'nvarchar(max)')
select '<![CDATA[' + @x.value('(/Value)[1]', 'nvarchar(max)') + ']]'
再次检查该列后,它似乎包含正确的信息。但是,我从未将其从 VARCHAR 改回 XML 或删除 CDATA 符号,即使在我检查该列时它们似乎已经消失。那我在这里错过了什么?这是正确的做法吗?
如果您需要完全控制生成 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
。
如果您确实需要 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 & <></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 & <></a>
<a>Text with forbidden character & <></a>
</root>
--回溯清楚地表明,XML内部不再有CDATA
SELECT CAST(@x AS VARCHAR(500));
<root>
<a>Normal Text</a>
<a>Text with forbidden character & <></a>
<a>Text with forbidden character & <></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 !) 是第三方或遗留要求。
请记住:如果您使用字符串连接,您只能以字符串格式存储 XML 和可读的 CDATA
。每当您将其转换为 XML 时, CDATA
将被忽略。使用 FOR XML EXPLICIT
允许类型安全存储,但对于更深的嵌套非常笨拙。这对于外部接口可能没问题,但你应该三思而后行...
相关答案的两个链接(我 :-)):
这是我的xml。我的目标是在导出时使用 CDATA 将数据包装在 Value 节点内,然后将其导入回 Xml 类型的列并移除 CDATA。
<Custom>
<Table>Shape</Table>
<Column>CustomScreen</Column>
<Value>Data</Value>
<Custom>
现在我正在用 table 中的 XML 替换值节点内的 '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><CustomPanel VisibilityIndicator="">&lText="No" Checked="False" Height="20" Width="50"/></Cell></Row></Table></CustomPanel></Value>
然后我正在做一些动态 sql 来更新该列
EXEC('UPDATE ['+ @tableName + '] SET [' + @columnName + '] = ''' + @nodeValue + ''' WHERE Shape_ID = ''' + @ShapeID + '''')
有人告诉我可以使用以下方法删除 CDATA,但我没有使用它。
declare @x xml
set @x=N'<Value><CustomPanel....... all the current info ...=""></Value>'
select @x.value('(/Value)[1]', 'nvarchar(max)')
select '<![CDATA[' + @x.value('(/Value)[1]', 'nvarchar(max)') + ']]'
再次检查该列后,它似乎包含正确的信息。但是,我从未将其从 VARCHAR 改回 XML 或删除 CDATA 符号,即使在我检查该列时它们似乎已经消失。那我在这里错过了什么?这是正确的做法吗?
如果您需要完全控制生成 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
。
如果您确实需要 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 & <></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 & <></a>
<a>Text with forbidden character & <></a>
</root>
--回溯清楚地表明,XML内部不再有CDATA
SELECT CAST(@x AS VARCHAR(500));
<root>
<a>Normal Text</a>
<a>Text with forbidden character & <></a>
<a>Text with forbidden character & <></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 !) 是第三方或遗留要求。
请记住:如果您使用字符串连接,您只能以字符串格式存储 XML 和可读的 CDATA
。每当您将其转换为 XML 时, CDATA
将被忽略。使用 FOR XML EXPLICIT
允许类型安全存储,但对于更深的嵌套非常笨拙。这对于外部接口可能没问题,但你应该三思而后行...
相关答案的两个链接(我 :-)):