2016-08-19 8 views
0

Это мой xml. Моя цель состоит в том, чтобы обернуть данные внутри узла Value с помощью CDATA при экспорте, а затем импортировать обратно в столбец типа Xml с удаленным CDATA.Обтекание и удаление CDATA вокруг XML

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

Сейчас я заменить «данные» внутри узла Value с 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>&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)') + ']]' 

После проверки колонки снова кажется, что она содержит правильную информацию. Однако я никогда не менял его обратно на XML из VARCHAR или удалял символы CDATA, даже если они, кажется, исчезли, когда я проверил столбец. Так что мне здесь не хватает? Это правильный способ сделать это?

+0

Btw: Эта строка 'SET @ OutputXML = replace (@OutputXML, ''. – Shnugo

ответ

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.

+0

Мне нравится этот ответ, +1 с моей стороны, но - AFAIC - нужно подумать дважды о том, как «CDATA» в XML. Это просто не нужно в большинстве случаев ... – Shnugo

+0

Я закончил выполнение конкатенации строк, хотя я пытался сделать это так, как вы написали. – DRockClimber

1

Если вам действительно нужен раздел внутри вашего XML CDATA, есть только два варианта

  • конкатенации (очень плохо)
  • FOR XML EXPLICIT (в этом случае вы получите ответ от Pawel)

Но вы должны принять во внимание, что раздел CDATA существует только для ленивого ввода. Существует абсолютно никакой разницы ли содержимое заключено в CDATA или правильно экранировано. Поэтому Microsoft решила не поддерживать синтаксис CDATA в современных методах XML. Это просто не нужно.

Посмотрите на эти примеры:

--i начать со строкой, содержащей то же самое содержание спасся и в 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>'; 

--This строка отливают в XML.

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

--This является выход, и вы можете видеть, что секция 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> 

--The резервного литье ясно показывает, что 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!) являются третьими лицами или устаревшими требованиями.

И имейте в виду: если вы используете конкатенацию строк, вы можете хранить XML с читаемым CDATA только в строковом формате. Всякий раз, когда вы передаете это в XML, CDATA будет пропущен. Использование FOR XML EXPLICIT позволяет создавать хранилища, но очень неуклюжие с более глубокими вложениями. Это может быть в порядке с внешним интерфейсом, но вы должны дважды подумать об этом ...

Две ссылки на соответствующие ответы (от меня :-)):

+0

@Schnugo Хорошая информация, как всегда. Я закончил выполнение конкатенации строк. У меня есть вопрос. Разве CData не полезен в случаях, когда может произойти «инъекция sql»? Мне сказали, что это поможет людям вставить случайные вещи. – DRockClimber