52

XML列(SQL SERVER 2008 R2)に挿入しようとしていますが、サーバーから次のようなメッセージが表示されます。

System.Data.SqlClient.SqlException(0x80131904):
XML解析:1行目、文字39、エンコーディングを切り替えることができません

挿入を成功させるには、XML列がUTF-16である必要があることがわかりました。

私が使用しているコードは次のとおりです。

 XmlSerializer serializer = new XmlSerializer(typeof(MyMessage));
 StringWriter str = new StringWriter();
 serializer.Serialize(str, message);
 string messageToLog = str.ToString();

オブジェクトをシリアル化してUTF-8文字列にする方法を教えてください。

編集:わかりました、混乱して申し訳ありません-文字列はUTF-8である必要があります。あなたは正しかった-デフォルトではUTF-16であり、UTF-8に挿入しようとすると合格します。したがって、問題はUTF-8にシリアル化する方法です。

これにより、SQLServerに挿入しようとしたときにエラーが発生します。

    <?xml version="1.0" encoding="utf-16"?>
    <MyMessage>Teno</MyMessage>

これはしません:

    <?xml version="1.0" encoding="utf-8"?>
    <MyMessage>Teno</MyMessage>

アップデート

XmlSQL Server 2008の列タイプにutf-8が必要な場合、およびencoding挿入しようとしているxml仕様のプロパティにutf-16がある場合を把握しました。

を追加する場合はutf-8、次のようにSQLコマンドにパラメーターを追加します。

 sqlcmd.Parameters.Add("ParamName", SqlDbType.VarChar).Value = xmlValueToAdd;

前の行にxmlValueToAddを追加しようとするとencoding=utf-16、挿入時にエラーが発生します。また、VarChar国の文字が認識されないことを意味します(疑問符として表示されます)。

utf-16をdbに追加するには、または前の例で使用するSqlDbType.NVarCharSqlDbType.Xml、タイプをまったく指定しないでください。

 sqlcmd.Parameters.Add(new SqlParameter("ParamName", xmlValueToAdd));
4

8 に答える 8

36

この質問は他の 2 つの質問とほぼ重複しており、驚くべきことに、これは最新のものですが、最良の回答が欠けていると思います。

重複と、私が彼らの最良の答えであると信じているものは次のとおりです。

XmlReader最終的には、アプリケーション サーバー内でローカルに解析できる限り、どのエンコーディングが宣言または使用されても問題ありません。

SQLサーバーのXML型列からADO.netでXMLを読み取る最も効率的な方法で確認されたように?、SQL Server は XML を効率的なバイナリ形式で格納します。このSqlXmlクラスを使用することで、ADO.net はこのバイナリ形式で SQL Server と通信でき、データベース サーバーで XML のシリアライズまたはデシリアライズを行う必要がなくなります。これにより、ネットワークを介した転送もより効率的になります。

を使用するSqlXmlと、XML が事前に解析されてデータベースに送信され、DB は文字エンコーディング (UTF-16 など) について何も知る必要がなくなります。特に、XML 宣言は、挿入に使用されるメソッドに関係なく、データベース内のデータと共に永続化されないことに注意してください。

これに非常によく似た方法については、上記のリンクの回答を参照してください。ただし、この例は私のものです。

using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Xml;

static class XmlDemo {
    static void Main(string[] args) {
        using(SqlConnection conn = new SqlConnection()) {
            conn.ConnectionString = "...";
            conn.Open();

            using(SqlCommand cmd = new SqlCommand("Insert Into TestData(Xml) Values (@Xml)", conn)) {

                cmd.Parameters.Add(new SqlParameter("@Xml", SqlDbType.Xml) {
                    // Works.
                    // Value = "<Test/>"

                    // Works.  XML Declaration is not persisted!
                    // Value = "<?xml version=\"1.0\"?><Test/>"

                    // Works.  XML Declaration is not persisted!
                    // Value = "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Test/>"

                    // Error ("unable to switch the encoding" SqlException).
                    // Value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>"

                    // Works.  XML Declaration is not persisted!
                    Value = new SqlXml(XmlReader.Create(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>")))
                });

                cmd.ExecuteNonQuery();
            }
        }
    }
}

最後の (コメントなしの) 例は「本番環境で使用できる」とは見なしませんが、簡潔で読みやすいようにそのままにしておきます。適切に実行された場合、完了時にメソッドが確実に呼び出されるように、ステートメント内でStringReaderthe と created の両方XmlReaderを初期化する必要があります。usingClose()

私が見たところ、XML 列を使用する場合、XML 宣言は永続化されません。たとえば、.NET を使用せずにこの直接 SQL 挿入ステートメントを使用しただけでは、XML 宣言は XML と共にデータベースに保存されません。

Insert Into TestData(Xml) Values ('<?xml version="1.0" encoding="UTF-8"?><Test/>');

OPの質問に関しては、シリアル化するオブジェクトをオブジェクトからXML構造に変換するMyMessage必要があり、これにXmlSerializerはまだ必要です。ただし、最悪の場合、文字列にシリアル化する代わりに、メッセージを にシリアル化することができます。これは、新しいものを介してXmlDocument渡すことができ、文字列への逆シリアル化/シリアル化トリップを回避します。(詳細と例については、http://blogs.msdn.com/b/jongallant/archive/2007/01/30/how-to-convert-xmldocument-to-xmlreader-for-sqlxml-data-type.aspxを参照してください.)SqlXmlXmlNodeReader

ここにあるものはすべて、.NET 4.0 および SQL Server 2008 R2 に対して開発およびテストされています。

ここや他の場所で他の回答に示されているように、追加の変換 (デシリアライゼーションとシリアライゼーション - DOM、文字列など) を介して XML を実行して無駄にしないでください。

于 2012-01-25T05:18:30.280 に答える
22

.net 文字列は常にエンコーディングUTF-16を使用してオブジェクトをシリアル化する必要がありますが。UTF-16それは次のようなものになるはずです:

public static string ToString(object source, Type type, Encoding encoding)
{
    // The string to hold the object content
    String content;

    // Create a memoryStream into which the data can be written and readed
    using (var stream = new MemoryStream())
    {
        // Create the xml serializer, the serializer needs to know the type
        // of the object that will be serialized
        var xmlSerializer = new XmlSerializer(type);

        // Create a XmlTextWriter to write the xml object source, we are going
        // to define the encoding in the constructor
        using (var writer = new XmlTextWriter(stream, encoding))
        {
            // Save the state of the object into the stream
            xmlSerializer.Serialize(writer, source);

            // Flush the stream
            writer.Flush();

            // Read the stream into a string
            using (var reader = new StreamReader(stream, encoding))
            {
                // Set the stream position to the begin
                stream.Position = 0;

                // Read the stream into a string
                content = reader.ReadToEnd();
            }
        }
    }

    // Return the xml string with the object content
    return content;
}

エンコーディングを Encoding.Unicode に設定すると、文字列が になるだけでなくUTF-16、xml 文字列も として取得する必要がありますUTF-16

<?xml version="1.0" encoding="utf-16"?>
于 2010-09-21T13:55:16.497 に答える
12

シリアライザーに XML 宣言を出力しないように指示するのが最も簡単な解決策ではないでしょうか? .NET と SQL は、それらの間で残りを整理する必要があります。

        XmlSerializer serializer = new XmlSerializer(typeof(MyMessage));
        StringWriter str = new StringWriter();
        using (XmlWriter writer = XmlWriter.Create(str, new XmlWriterSettings { OmitXmlDeclaration = true }))
        {
            serializer.Serialize(writer, message);
        }
        string messageToLog = str.ToString();
于 2013-01-29T09:29:41.790 に答える
12

この問題を解決するのに永遠にかかりました。

INSERT次のようなステートメントを SQL Serverに実行していました。

UPDATE Customers 
SET data = '<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';

これによりエラーが発生します:

メッセージ 9402、レベル 16、状態 1、行 2
XML 解析: 行 1、文字 39、エンコードを切り替えることができません

そして、本当に、非常に簡単な修正は次のとおりです。

UPDATE Customers 
SET data = N'<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';

違いは、Unicode 文字列の前にN:

N '<?xml version="1.0" encoding="utf-16"?>Teno</MyMessage>'

前者の場合、プレフィックスのない文字列は varchar と見なされます (例: Windows-1252 コード ページ)。文字列内で が検出されると、競合が発生します (文字列がutf-16ではないencoding="utf-16"ため、競合が発生します)。

修正は、文字列をnvarchar (つまり UTF-16)として SQL サーバーに渡すことです。

N '<?xml version="1.0" encoding="utf-16"?>'

そうすれば、文字列UTF-16 になり、XML が示す utf-16 エンコーディングと一致します。いわば、カーペットはカーテンにマッチします。

于 2016-02-22T16:48:23.610 に答える
5

.NETでは文字列は常にUTF-16であるため、管理対象アプリ内にいる限り、文字列がどのエンコーディングであるかを気にする必要はありません。

この問題は、SQLサーバーと通信する場合に発生する可能性が高くなります。あなたの質問はそのコードを示していないので、正確なエラーを特定するのは難しいです。私の提案は、サーバーに送信されるデータのエンコードを指定する、そのコードに設定できるプロパティまたは属性があるかどうかを確認することです。

于 2010-09-21T13:40:14.837 に答える
1

バイト配列ではなく文字列にシリアル化するため、この時点ではまだエンコードは行われていません。

「messageToLog」の始まりはどのように見えますか?XMLは、後で間違っていることが判明したエンコーディング(utf-8など)を指定していますか?

編集

さらに詳しい情報に基づくと、文字列はデータベースに渡されるときに自動的にutf-8に変換されるようですが、XML宣言でutf-16であると示されているため、データベースがチョークします。

この場合、utf-8にシリアル化する必要はありません。XMLから「encoding=」を省略してシリアル化する必要があります。XmlFragmentWriter(.Net、Google itの標準部分ではありません)を使用すると、これを実行できます。

于 2010-09-21T13:43:37.680 に答える
0

xmlシリアライザーのデフォルトのエンコードはUTF-16である必要があります。試してみることができることを確認するためだけに-

XmlSerializer serializer = new XmlSerializer(typeof(YourObject));

// create a MemoryStream here, we are just working
// exclusively in memory
System.IO.Stream stream = new System.IO.MemoryStream();

// The XmlTextWriter takes a stream and encoding
// as one of its constructors
System.Xml.XmlTextWriter xtWriter = new System.Xml.XmlTextWriter(stream, Encoding.UTF16);

serializer.Serialize(xtWriter, yourObjectInstance);

xtWriter.Flush();
于 2010-09-21T13:42:43.300 に答える