Обертывание и удаление CDATA вокруг XML
Это мой xml. Моя цель - обернуть данные в узле Value с помощью CDATA при экспорте, а затем импортировать их обратно в столбец типа XML с удаленным CDATA.
<Custom>
<Table>Shape</Table>
<Column>CustomScreen</Column>
<Value>Data</Value>
<Custom>
Прямо сейчас я заменяю "Данные" внутри узла "Значение" на XML из таблицы, а затем полагаю, что я помещаю в него 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)') + ']]'
После проверки столбца снова кажется, что он содержит правильную информацию. Однако я никогда не изменял его обратно на XML из VARCHAR и не удалял символы CDATA, хотя они, кажется, исчезли, когда я проверял столбец. Так чего мне здесь не хватает? Это правильный способ сделать это?
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
,
Если вам действительно нужно CDATA
раздел в вашем XML, есть только два варианта
- конкатенация строк (очень плохо)
FOR XML EXPLICIT
(в этом случае вы получили ответ от Павла)
Но вы должны принять во внимание, что CDATA
раздел существует только для ленивого ввода. Нет абсолютно никакой разницы, заключено ли содержимое в CDATA
раздел или правильно сбежал. Поэтому Microsoft решила даже не поддерживать CDATA
синтаксис в современных методах XML. Это просто не нужно.
Посмотрите на эти примеры:
- Я начинаю со строки, содержащей тот же контент, и 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
допускает безопасное хранение, но очень неуклюже с более глубокими вложениями. Это может быть нормально с внешним интерфейсом, но вы должны дважды подумать об этом...
Две ссылки на похожие ответы (от меня:-)):