Eu preciso cavar em uma tabela de logs com um esquema semelhante a este:
CREATE TABLE t (
id int PRIMARY KEY,
data varchar(max)
);
A coluna data
armazena um texto XML recebido de um serviço da web neste formato:
Esta é uma versão reduzida
<?xml version="1.0" encoding="UTF-8"?>
<PARAM>
<TAB DIM="30" ID="ZC3D2_1" SIZE="5">
<LIN NUM = "1">
<FLD NAME = "ZDOC" TYPE = "Char">Ferran López</FLD>
</LIN>
</TAB>
</PARAM>
Quando tento converter este texto para XML, recebo o próximo erro:
Análise XML: linha xx, caractere 48, caractere xml ilegal
Isso pode ser resolvido removendo a <xml>
tag, ou pelo menos, o encoding
atributo.
NOTA: Funciona bem se não houver caracteres especiais como
ó
, mesmo se eu não remover a<xml>
tag.
Pergunta
Existe uma maneira de convertê-lo em XML sem substituir ou remover a <xml>
tag?
CAST(REPLACE(data, 'encoding="UTF-8"', '') as XML)
db<>fique aqui
ATUALIZAR
O agrupamento do servidor é: Latin1_General_BIN
Mas mesmo se eu tentar alterar o agrupamento para o agrupamento usual de servidores, ele não funcionará.
SELECT
id,
CAST((data COLLATE Latin1_General_CI_AS) as XML)
FROM
t;
Seu XML armazenado em uma coluna varchar(max) deve ficar assim.
O
ó
deve ser representado com um valor de byte duploó
.Se você não tiver uma string codificada em UTF-8 armazenada em sua coluna, a maneira correta de fazer isso é remover a codificação do XML antes de converter o valor para o tipo de dados XML.
Acho que você tem um problema mais profundo. O UTF-8 permite mais caracteres do que os agrupamentos não Unicode regulares no SQL Server. Portanto, para estar seguro, você deve usar o SQL Server 2019, que possui agrupamentos UTF-8 (e eu entendo se isso não for viável / desejável por muitos motivos) ou use (tente) nvarchar em vez de varchar.
Se você tem medo do aumento de armazenamento indo de varchar para nvarchar, você pode usar a compactação de linha. Mas isso requer Enterprise Edition anterior ao SQL Server 2016.
O que está acontecendo aqui é:
XML
tipo armazena dados internamente como UTF-16 Little Endian (na maioria das vezes, pelo menos). Não importa qual seja a codificação de origem, o resultado final será UTF-16 LE (e sem<xml>
tag, portanto, nãoencoding="..."
).XML
:NVARCHAR
os dados são assumidos como UTF-16 LE. Se houver uma<xml>
tag e ela contiver oencoding
atributo, o único valor válido será"UTF-16"
.VARCHAR
os dados são considerados na página de código de 8 bits associada ao agrupamento dos dados quando não há<xml>
tag, ou se existe, mas não háencoding
atributo. Caso contrário, os dados serão interpretados como codificados na página de código especificada noencoding
atributo (mesmo que estejam codificados na página de código associada ao agrupamento dos dados).Latin1_General_BIN
, é seguro -suficiente para assumir no momento que a coluna está usando o mesmo agrupamento).ó
caractere na página de código Windows-1252 é: 0xF3 .<xml>
tag, no entanto, está declarando que os dados XML estão codificados como UTF-8.p
, que tem um valor de 0x70 . Portanto, você obtém o erro "caractere xml ilegal" (porqueencoding="UTF-8"
informa à função de conversão que os bytes são bytes UTF-8 válidos; a conversão não vê oó
caractere).Suas opções são:
Idealmente, a coluna seria convertida
XML
e oencoding
atributo da<xml>
tag, ou a tag inteira<xml>
, seria removida no caminho. E, o tipo deXML
dados pode economizar espaço se houver nomes de elemento e/ou atributo repetidos, pois cria uma dicionário (lista de pesquisa) de nomes internamente e registra a estrutura usando os valores de ID.Defina a
[data]
coluna para usar um agrupamento UTF-8 (novo no SQL Server 2019, portanto, não é uma opção para você)Defina a
[data]
coluna como eNVARCHAR
remova o atributo da tag ou a tag inteira .encoding
<xml>
<xml>
Converta a string de entrada em bytes UTF-8. Portanto, o
ó
caractere é de dois bytes em UTF-8: 0xC3B3 , que aparece comoó
no Windows-1252.NOTAS:
encoding
atributo da tag, ou a tag<xml>
inteira , não é uma opção. Claro, ele funcionará neste caso específico, mas não funcionará em todos os casos devido à coluna e aos agrupamentos UTF-8 não estarem disponíveis no SQL Server 2014. Portanto, quaisquer caracteres Unicode não disponíveis na página de código 1252 do Windows ser convertido para ou (dependendo do caractere BMP ou do caractere suplementar):<xml>
VARCHAR
?
??
ó
caractere não está disponível na página de código cirílico. Mas, há um mapeamento "Best Fit" e é por isso que acabamos com umo
em vez de um?
._100_
agrupamentos de nível. Além disso, qualquer pessoa que esteja trabalhando no SQL Server 2012 ou mais recente deve, idealmente, usar a_100_
ordenação de nível que termina com_SC
(para caracteres suplementares). Finalmente, ao precisar de um agrupamento binário no SQL Server 2005 ou mais recente, use um que termine em_BIN2
(veja meu post aqui sobre o motivo).use um agrupamento compatível para seu varchar
db<>fique aqui