Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 如何解决;无法切换编码";将XML插入SQL Server时出错_.net_Sql Server_Xml_Utf 8_Utf 16 - Fatal编程技术网

.net 如何解决;无法切换编码";将XML插入SQL Server时出错

.net 如何解决;无法切换编码";将XML插入SQL Server时出错,.net,sql-server,xml,utf-8,utf-16,.net,Sql Server,Xml,Utf 8,Utf 16,我试图插入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(); ser

我试图插入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

示例

这会在尝试插入SQL Server时导致错误:

    <?xml version="1.0" encoding="utf-16"?>
    <MyMessage>Teno</MyMessage>
如果尝试在前一行中添加xmlValueToAdd和
encoding=utf-16
,则会在insert中产生错误。另外,
VarChar
表示不能识别国家字符(它们变成问号)

要将utf-16添加到db,请在前面的示例中使用
SqlDbType.NVarChar
SqlDbType.Xml
,或者根本不指定类型:

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

在.NET中,字符串始终是UTF-16,因此只要您留在托管应用程序中,就不必关心它是哪种编码


问题更可能出现在与SQL server对话的地方。您的问题没有显示该代码,因此很难确定确切的错误。我的建议是,检查是否有可以在该代码上设置的属性或属性指定发送到服务器的数据的编码。

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();

您正在序列化为字符串而不是字节数组,因此,此时还没有进行任何编码

“messageToLog”的开头是什么样子的?XML是否指定了后来证明是错误的编码(例如utf-8)

编辑

根据您的进一步信息,当字符串被传递到数据库时,它听起来像是自动转换为utf-8,但是数据库阻塞了,因为XML声明说它是utf-16


在这种情况下,您不需要序列化到utf-8。您需要使用XML中省略的“encoding=”进行序列化。XmlFragmentWriter(不是.Net的标准部分,Google it)允许您这样做。

尽管.Net字符串总是
UTF-16
,但您需要使用
UTF-16
编码序列化对象。 应该是这样的:

 sqlcmd.Parameters.Add("ParamName", SqlDbType.VarChar).Value = xmlValueToAdd;
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设置为encoding.Unicode,不仅字符串将是
UTF-16
,而且您还应该将xml字符串设置为
UTF-16

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

这个问题与其他两个问题几乎是重复的,令人惊讶的是——虽然这是最近的一个问题——我相信它没有找到最好的答案

我认为他们最好的答案是:

  • (2009-10-14)
  • (2008-12-21)
最后,声明或使用什么编码并不重要,只要用户可以在应用服务器中本地解析它

正如中所确认的,SQL Server以高效的二进制格式存储XML。通过使用该类,ADO.net可以以这种二进制格式与SQL Server通信,而不需要数据库服务器对XML进行任何序列化或反序列化。这对于跨网络传输也应该更有效


通过使用
SqlXml
,XML将被预先解析发送到数据库,然后数据库不需要知道任何字符编码-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();
            }
        }
    }
}
现在就OP的问题而言,要序列化的对象仍然需要从
MyMessage
对象转换为XML结构,并且仍然需要
XmlSerializer
。但是,在最坏的情况下,可以将消息序列化为
XmlDocument
,而不是序列化为字符串,这样就可以通过新的字符串传递给
SqlXml
,从而避免对字符串进行反序列化/序列化。(有关详细信息和示例,请参见。)

这里的一切都是根据.NET4.0和SQLServer2008R2开发和测试的


请不要浪费通过额外转换(反序列化和序列化-到DOM、字符串或其他)来运行XML,如这里和其他地方的其他答案所示。

告诉序列化程序不要输出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();

我花了很长时间才重新解决这个问题

我在SQL Server中执行一个
INSERT
语句,类似于:

UPDATE Customers 
SET data = '<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';
更新客户
设置数据='Teno';
这就产生了错误:

Msg 9402,第16级,状态1,第2行
XML解析:第1行,字符39,无法切换编码

真正非常简单的解决方法是:

UPDATE Customers 
SET data = N'<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';
更新客户
设置数据=N'Teno';
区别在于在Unicode字符串前面加上
N

N'Teno'

在前一种情况下,假定未固定的字符串为varchar(例如,Windows-1252代码页)。当它在字符串中遇到
encoding=“utf-16”
时,会发生冲突(这是正确的,因为字符串不是utf-16)

修复方法是将字符串作为一个nvarchar(即UTF-16)传递给SQL server:

N'

这样,字符串就是UTF-16,它与UTF-16编码t匹配
UPDATE Customers 
SET data = '<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';
UPDATE Customers 
SET data = N'<?xml version="1.0" encoding="utf-16"?><MyMessage>Teno</MyMessage>';