SQL Server altera a estrutura XML quando inserido

15

Estou inserindo alguns dados XML em uma coluna XML no SQL Server, mas depois que os dados foram inseridos, eles foram alterados pelo SQL Server. Aqui estão os dados que eu insiro

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

Quando eu leio de volta, fica assim

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

Preste atenção na segunda linha. Este é um problema porque altera como será a saída da transformação XSLT. O primeiro exemplo criará um espaço entre o nome e o sobrenome, enquanto o segundo não criará espaço, portanto será como JohnJohnsen, enquanto o primeiro será como John Johnsen.

Existe alguma maneira de resolver isso?

Zach
fonte
É um problema, porque isso muda como será a saída da transformação XSLT. Primeira linha irá criar um espaço entre dado e nome de família, enquanto o segundo não irá criar qualquer espaço entre, por isso vai ser como JohnJohnsen, enquanto o primeiro vai ser como John Johnsen
Sr. Zach
hmhm, é o espaço apropriado "", mas não é apenas um espaço como neste comentário (você não pode vê-lo)
a_vlad 27/11/18
1
Talvez você possa usar um caractere de controle que não exista nos dados (como _ou ~) e substituí-lo por um espaço no momento da apresentação.
Aaron Bertrand

Respostas:

25

Você pode usar xml:space = "preserve"nos nós em que deseja manter o espaço. Usar xml: space é "apenas um sinal de intenção", mas o SQL Server é gentil conosco aqui.

Para um nó

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

select @X;

Resultado:

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

Documento completo:

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

select @X;

Resultado:

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

Outra opção para todo o documento é usar converter com o estilo 1 .

Preserve espaço em branco insignificante. Essa configuração de estilo define a manipulação xml: space padrão para corresponder ao comportamento de xml: space = "preserve".

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

select @X;
Mikael Eriksson
fonte
Interessante que isso é necessário. Não deve ser tarefa do SQL Server decidir qual espaço em branco é "insignificante" e removê-lo silenciosamente sem modificações no documento!
Lightness Races com Monica
3
@LightnessRacesinOrbit Estou muito feliz com a implementação do SQL Server. A formatação (espaço em branco) em XML não é considerada importante até que você diga. Ter um olhar para este exemplo para ver o número de nós que são realmente no documento e que ele faz ao tamanho de armazenamento ..
Mikael Eriksson
3
Considero uma violação de especificação, porque aqui os dados são aceitos como XML e armazenados como XML, sem manipulação ou transformação ou qualquer outra forma de travessuras da camada XML além de simplesmente armazenar o documento (ostensivamente), portanto o comportamento deve caem no de um "processador" em vez de um "aplicativo" e, portanto, não devem remover o espaço em branco .
Lightness Races com Monica
9

Esta página da documentação do SQL Server diz

Os dados são armazenados em uma representação interna que ... pode não ser uma cópia idêntica do XML do texto, porque as seguintes informações não são mantidas: espaços em branco insignificantes, ordem dos atributos, prefixos de namespace e declaração XML.

Para o seu exemplo, suponho que o espaço em branco da tag do meio não seja significativo e, portanto, livre para refatorar a representação. Eu não acho que haja uma correção para isso; é exatamente como o SQL Server implementa o tipo de dados XML.

As soluções alternativas incluiriam o uso de um marcador de posição em vez do espaço em branco, como diz @Aaron. O consumidor deve se lembrar de inserir e retirar esses tokens. Como alternativa, defina a coluna como nvarchar em vez de XML. Definitivamente, isso preservará todo o espaço em branco e qualquer outra formatação. Um exemplo rápido:

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 />  

A coluna nvarchar preserva o formato de entrada, a coluna XML não.

Você perderá a capacidade de usar XPATH em consultas SQL. Se o XML for fragmentado apenas no aplicativo, isso é irrelevante. Além disso, a cadeia de caracteres pode ser compactada economizando espaço no banco de dados, se isso for significativo para você.

Michael Green
fonte
Provavelmente, você ainda pode usar o XPATH em consultas contra a versão XML, mesmo que você deixe a reformatação, contanto que não esteja confiando em uma ocorrência (ou falha) para o espaço insignificante lá.
Aaron Bertrand
0

Você pode agrupar seu espaço CDATAao armazenar os dados:

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

Parece que o SQL server mantém o espaço internamente, mas remove a CDATAmarcação desnecessária quando o resultado é usado novamente SELECT. Felizmente, o espaço é mantido ao reutilizar o resultado de tal SELECT:

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

SET @Y = (SELECT @X)

SELECT @Y

O resultado será:

<text> </text>
Bruno
fonte
Também tentei o CDATA, mas também foi removido.
Zach
O próprio @MrZach CDATA é removido, mas o espaço permanece. (Tentou em SQL Express 2016.)
de Bruno
Estranho, aqui o espaço foi removido. Pense também em expressar 2016 ou 2017
Mr Zach