Atualmente, estou procurando uma maneira fácil de serializar objetos (em C # 3).
Pesquisei alguns exemplos e encontrei algo como:
MemoryStream memoryStream = new MemoryStream ( );
XmlSerializer xs = new XmlSerializer ( typeof ( MyObject) );
XmlTextWriter xmlTextWriter = new XmlTextWriter ( memoryStream, Encoding.UTF8 );
xs.Serialize ( xmlTextWriter, myObject);
string result = Encoding.UTF8.GetString(memoryStream .ToArray());
Depois de ler esta pergunta eu me perguntei, por que não usar StringWriter? Parece muito mais fácil.
XmlSerializer ser = new XmlSerializer(typeof(MyObject));
StringWriter writer = new StringWriter();
ser.Serialize(writer, myObject);
serializedValue = writer.ToString();
Outro problema era que o primeiro exemplo gerou XML que não pude simplesmente escrever em uma coluna XML do SQL Server 2005 DB.
A primeira pergunta é: Existe uma razão pela qual eu não devo usar StringWriter para serializar um Object quando eu preciso dele como uma string posteriormente? Nunca encontrei um resultado usando StringWriter ao pesquisar no Google.
A segunda é, claro: se você não deveria fazer isso com StringWriter (por qualquer motivo), qual seria uma maneira correta e correta?
Adição:
Como já foi mencionado por ambas as respostas, irei mais adiante no problema de XML para banco de dados.
Ao gravar no banco de dados, obtive a seguinte exceção:
System.Data.SqlClient.SqlException: análise XML: linha 1, caractere 38, incapaz de mudar a codificação
Para corda
<?xml version="1.0" encoding="utf-8"?><test/>
Peguei a string criada no XmlTextWriter e coloquei como xml lá. Este não funcionou (nem com inserção manual no BD).
Depois, tentei a inserção manual (apenas escrevendo INSERT INTO ...) com encoding = "utf-16" que também falhou. Remover a codificação funcionou totalmente então. Depois desse resultado, voltei para o código StringWriter e pronto - funcionou.
Problema: eu realmente não entendo por quê.
at Christian Hayter: Com esses testes, não tenho certeza se devo usar utf-16 para escrever no DB. Definir a codificação para UTF-16 (na tag xml) não funcionaria então?
fonte
Respostas:
<TL; DR> O problema é bastante simples, na verdade: você não está combinando a codificação declarada (na declaração XML) com o tipo de dados do parâmetro de entrada. Se você adicionou manualmente
<?xml version="1.0" encoding="utf-8"?><test/>
à string, declararSqlParameter
que é do tipoSqlDbType.Xml
ouSqlDbType.NVarChar
geraria o erro "não foi possível mudar a codificação". Então, ao inserir manualmente via T-SQL, uma vez que você mudou a codificação declarada para serutf-16
, você estava claramente inserindo umaVARCHAR
string (não prefixada com um "N" maiúsculo, portanto, uma codificação de 8 bits, como UTF-8) e não umaNVARCHAR
string (prefixada com um "N" maiúsculo, daí a codificação UTF-16 LE de 16 bits).A correção deveria ter sido tão simples quanto:
encoding="utf-8"
: simplesmente não adicione a declaração XML.encoding="utf-16"
: ouSqlDbType.NVarChar
vez deSqlDbType.VarChar
:-) (ou, possivelmente, mude para usarSqlDbType.Xml
)(A resposta detalhada está abaixo)
Todas as respostas aqui são muito complicadas e desnecessárias (independentemente dos 121 e 184 votos positivos para as respostas de Christian e Jon, respectivamente). Eles podem fornecer código funcional, mas nenhum deles realmente responde à pergunta. O problema é que ninguém realmente entendeu a questão, que em última análise é sobre como funciona o tipo de dados XML no SQL Server. Nada contra essas duas pessoas claramente inteligentes, mas essa questão tem pouco ou nada a ver com a serialização para XML. Salvar dados XML no SQL Server é muito mais fácil do que o que está implícito aqui.
Realmente não importa como o XML é produzido, desde que você siga as regras de como criar dados XML no SQL Server. Eu tenho uma explicação mais completa (incluindo código de exemplo de trabalho para ilustrar os pontos descritos abaixo) em uma resposta a esta pergunta: Como resolver o erro “não foi possível mudar a codificação” ao inserir XML no SQL Server , mas o básico é:
NVARCHAR(MAX)
ouXML
/SqlDbType.NVarChar
(maxsize = -1) ouSqlDbType.Xml
, ou se estiver usando um literal de string, ele deve ser prefixado com um "N" maiúsculo.VARCHAR(MAX)
/SqlDbType.VarChar
(maxsize = -1), ou se utilizando uma cadeia de caracteres, então ele deve não ser prefixado com uma letra maiúscula "N".Com os pontos descritos acima em mente, e considerando que as strings em .NET são sempre UTF-16 LE / UCS-2 LE (não há diferença entre elas em termos de codificação), podemos responder às suas perguntas:
Não, seu
StringWriter
código parece estar bom (pelo menos não vejo problemas em meu teste limitado usando o segundo bloco de código da pergunta).Não é necessário fornecer a declaração XML. Quando está ausente, a codificação é considerada UTF-16 LE se você passar a string para o SQL Server como
NVARCHAR
(ou sejaSqlDbType.NVarChar
) ouXML
( ou sejaSqlDbType.Xml
). A codificação é considerada a página de código padrão de 8 bits se for passada comoVARCHAR
(ou sejaSqlDbType.VarChar
). Se você tiver caracteres ASCII não padrão (ou seja, valores 128 e acima) e estiver transmitindo comoVARCHAR
, provavelmente verá "?" para caracteres BMP e "??" para caracteres suplementares, pois o SQL Server converterá a string UTF-16 do .NET em uma string de 8 bits da página de código do banco de dados atual antes de convertê-la novamente em UTF-16 / UCS-2. Mas você não deve receber nenhum erro.Por outro lado, se você especificar a declaração XML, deverá passar para o SQL Server usando o tipo de dados correspondente de 8 ou 16 bits. Portanto, se você tiver uma declaração afirmando que a codificação é UCS-2 ou UTF-16, deverá passar como
SqlDbType.NVarChar
ouSqlDbType.Xml
. Ou, se você tem uma declaração de que a codificação é uma das opções de 8 bits (ou sejaUTF-8
,Windows-1252
,iso-8859-1
, etc), então você deve passar em comoSqlDbType.VarChar
. A falha em combinar a codificação declarada com o tipo de dados SQL Server de 8 ou 16 bits adequado resultará no erro "não foi possível alternar a codificação" que você estava recebendo.Por exemplo, usando seu
StringWriter
código de serialização baseado em seu , eu simplesmente imprimi a string resultante do XML e a usei no SSMS. Como você pode ver abaixo, a declaração XML está incluída (porqueStringWriter
não tem a opção deOmitXmlDeclaration
likeXmlWriter
faz), o que não representa nenhum problema, desde que você passe a string como o tipo de dados correto do SQL Server:Como você pode ver, ele até lida com caracteres além do ASCII padrão, visto que
ሴ
é o ponto de código BMP U + 1234 e😸
é o ponto de código de caractere suplementar U + 1F638. No entanto, o seguinte:resulta no seguinte erro:
Portanto, toda essa explicação à parte, a solução completa para sua pergunta original é:
Você estava claramente passando a corda como
SqlDbType.VarChar
. Alterne paraSqlDbType.NVarChar
e ele funcionará sem a necessidade de passar pela etapa extra de remoção da declaração XML. Isso é preferível a manterSqlDbType.VarChar
e remover a declaração XML porque esta solução evitará a perda de dados quando o XML incluir caracteres ASCII não padrão. Por exemplo:Como você pode ver, não há erro desta vez, mas agora há perda de dados 🙀.
fonte
SqlDbType.NVarChar
ouXml
.Um problema
StringWriter
é que, por padrão, ele não permite que você defina a codificação que anuncia - então você pode acabar com um documento XML anunciando sua codificação como UTF-16, o que significa que você precisa codificá-lo como UTF-16 se você escreva em um arquivo. Eu tenho uma pequena classe para ajudar com isso:Ou se você só precisar de UTF-8 (que é tudo de que preciso frequentemente):
Quanto ao motivo pelo qual você não pôde salvar seu XML no banco de dados - você terá que nos dar mais detalhes sobre o que aconteceu quando você tentou, se quiser que possamos diagnosticar / consertar.
fonte
StringWriter
não leva em conta a codificação, mas nunca menos, obrigado por um método bacana :)MemoryStream
aStreamWriter
com a codificação correta.StreamWriter
é umTextWriter
(o tipoXmlWriter.Create
esperado) com codificação personalizável, afinal.Ao serializar um documento XML em uma string .NET, a codificação deve ser definida como UTF-16. As strings são armazenadas como UTF-16 internamente, portanto, esta é a única codificação que faz sentido. Se você quiser armazenar dados em uma codificação diferente, use uma matriz de bytes.
O SQL Server funciona em um princípio semelhante; qualquer string passada em uma
xml
coluna deve ser codificada como UTF-16. O SQL Server rejeitará qualquer string em que a declaração XML não especifique UTF-16. Se a declaração XML não estiver presente, o padrão XML exige que o padrão seja UTF-8, portanto, o SQL Server também rejeitará isso.Tendo isso em mente, aqui estão alguns métodos utilitários para fazer a conversão.
fonte
StringWriter
esperado. Veja minha resposta. O formato de armazenamento interno é irrelevante aqui.Nothing
é implicitamente conversível para qualquer tipo. Eu corrigi oDeserialize
código. OSerialize
aviso deve ser apenas um Resharper, o compilador por si só não faz objeções e é legal fazer isso.Em primeiro lugar, tome cuidado para não encontrar exemplos antigos. Você encontrou um que usa
XmlTextWriter
, que está obsoleto a partir do .NET 2.0.XmlWriter.Create
deve ser usado em seu lugar.Aqui está um exemplo de serialização de um objeto em uma coluna XML:
fonte
XmlReader
possa analisá-la. Ele será enviado pré-analisado para o banco de dados, e então o DB não precisa saber nada sobre codificação de caracteres - UTF-16 ou outro. Em particular, observe que as declarações XML nem mesmo são persistentes com os dados no banco de dados, independentemente do método usado para inseri-los. Não desperdice executando XML por meio de conversões extras, conforme mostrado em outras respostas aqui e em outros lugares.fonte
Pode ter sido abordado em outro lugar, mas simplesmente alterar a linha de codificação da fonte XML para 'utf-16' permite que o XML seja inserido em um tipo de dados xml do SQL Server.
O resultado é que todo o texto XML é inserido no campo de tipo de dados 'xml', mas a linha 'cabeçalho' é removida. O que você vê no registro resultante é apenas
Usar o método de serialização descrito na entrada "Respondido" é uma maneira de incluir o cabeçalho original no campo de destino, mas o resultado é que o texto XML restante é colocado em um XML
<string></string>
tag .O adaptador de tabela no código é uma classe construída automaticamente usando o Visual Studio 2013 "Add New Data Source: wizard. Os cinco parâmetros para o método Insert mapeiam para campos em uma tabela SQL Server.
fonte