SQL-сервер изменяет структуру XML при вставке

15

Я вставляю некоторые данные XML в столбец XML на сервере SQL, но после того, как данные были вставлены, они были изменены сервером SQL. Вот данные, которые я вставляю

              <xsl:value-of select="name/n/given" />
            <xsl:text> </xsl:text>
          <xsl:value-of select="name/n/family" />

Когда я читаю это обратно, это выглядит так

              <xsl:value-of select="name/n/given" />
          <xsl:text />
          <xsl:value-of select="name/n/family" />

Обратите внимание на вторую строку. Это проблема, потому что она меняет то, каким будет вывод XSLT-преобразования. Первый пример создаст пробел между именем и фамилией, а второй не создаст пробела, поэтому он будет похож на JohnJohnsen, а первый будет похож на John Johnsen.

Есть ли способ решить эту проблему?

Мистер Зак
источник
Это проблема, потому что это меняет то, как будет вывод XSLT-преобразования. Первая строка создаст пробел между именем и фамилией, в то время как вторая не создаст пробела между ними, поэтому будет как Джон Джонсон, а первая будет как Джон Джонсен
г-н Зак
хм, правильный пробел это "", а не просто пробел, как в этом комментарии (вы не можете видеть это)
a_vlad
1
Возможно, вы могли бы использовать управляющий символ, который не существует в данных (например, _или ~), а затем заменить его пробелом во время представления.
Аарон Бертран

Ответы:

25

Вы можете использовать xml:space = "preserve"на узлах, где вы хотите сохранить пространство. Использование xml: space - «всего лишь сигнал о намерениях», но SQL-сервер очень добр к нам.

Для одного узла

declare @X xml =
'<root>
  <element xml:space = "preserve"> </element>
  <element> </element>
</root>'

select @X;

Результат:

<root>
  <element xml:space="preserve"> </element>
  <element />
</root>

Весь документ:

declare @X xml =
'<root xml:space = "preserve">
  <element> </element>
  <element> </element>
</root>'

select @X;

Результат:

<root xml:space="preserve">
  <element> </element>
  <element> </element>
</root>

Еще один вариант для всего документа - использовать конвертировать со стилем 1 .

Сохраните незначительный пробел. Этот параметр стиля устанавливает стандартную обработку xml: space для соответствия поведению xml: space = "preserve".

declare @X xml = convert(xml, 
'<root>
  <element> </element>
  <element> </element>
</root>', 1)

select @X;
Микаэль Эрикссон
источник
Интересно, что это требуется. SQL Server не должен решать, какие пробелы являются «незначительными», и молча удалять их без изменений документа!
Легкость гонки с Моникой
3
@LightnessRacesinOrbit Я вполне доволен реализацией SQL Server. Форматирование (пробелы) в XML не считается важным, пока вы не скажете, что это так. Посмотрите на этот пример, чтобы увидеть количество узлов, которые на самом деле находятся в документе, и что это делает с размером хранилища ..
Микаэль Эрикссон,
3
Я считаю, что это нарушение спецификации, потому что здесь данные принимаются как XML и хранятся как XML, без каких-либо манипуляций, преобразований или любых других форм махинаций на уровне XML, кроме простого хранения документа (якобы), поэтому поведение должно относятся к «процессору», а не «приложению», и поэтому не должны удалять пробелы .
Легкость гонки с Моникой
9

На этой странице документации по SQL Server написано

Данные хранятся во внутреннем представлении, которое ... не может быть идентичной копией текстового XML, потому что не сохраняется следующая информация: незначительные пробелы, порядок атрибутов, префиксы пространств имен и объявление XML.

Для вашего примера я предполагаю, что он считает пробел среднего тега несущественным и, следовательно, свободен для рефакторинга представления. Я не думаю, что есть решение для этого; это просто, как SQL Server реализует тип данных XML.

Обходные пути включали бы использование заполнителя вместо пробела, как говорит @Aaron. Потребитель должен помнить, чтобы вставить и раздеть эти жетоны. В качестве альтернативы определите столбец как nvarchar вместо XML. Это определенно сохранит все пробелы и любое другое форматирование. Быстрый пример:

create table x(i nvarchar(99), j xml);
insert x values ('<a> </a>', '<a> </a>');  -- note the space
select * from x

i           j
----------  -------
<a> </a>    <a />  

Столбец nvarchar сохраняет формат ввода, а столбец XML - нет.

Вы потеряете возможность использовать XPATH в запросах SQL. Если XML только уничтожен в приложении, это не имеет значения. Кроме того, символьная строка может быть сжата, сохраняя место в БД, если это важно для вас.

Майкл Грин
источник
Возможно, вы все еще можете использовать XPATH в запросах к версии XML, даже если вы просто дадите ей переформатироваться, если вы не полагаетесь на попадание (или промах) для незначительного места в нем.
Аарон Бертран
0

Вы можете обернуть свое пространство CDATAпри хранении данных:

<xsl:text><![CDATA[ ]]></xsl:text>

Похоже, что SQL-сервер сохраняет внутреннее пространство, но удаляет ненужную CDATAразметку при возврате результата с помощью SELECT. К счастью, место сохраняется при повторном использовании результата такого SELECT:

DECLARE @X XML = '<text><![CDATA[ ]]></text>'
DECLARE @Y XML

SET @Y = (SELECT @X)

SELECT @Y

Результат будет:

<text> </text>
Bruno
источник
Также попробовал CDATA, но он также был удален.
г-н Зак
@MrZach CDATA сама удаляется, но место остается. (Пробовал в SQL Express 2016.)
Бруно
Странно, здесь место убрали. Подумайте также, экспресс 2016 или 2017
г-н Зак