I'm currently searching for an easy way to serialize objects (in C# 3).
I googled some examples and came up with something like:
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());
After reading this question I asked myself, why not using StringWriter? It seems much easier.
XmlSerializer ser = new XmlSerializer(typeof(MyObject));
StringWriter writer = new StringWriter();
ser.Serialize(writer, myObject);
serializedValue = writer.ToString();
Another Problem was, that the first example generated XML I could not just write into an XML column of SQL Server 2005 DB.
The first question is: Is there a reason why I shouldn't use StringWriter to serialize an Object when I need it as a string afterwards? I never found a result using StringWriter when googling.
The second is, of course: If you should not do it with StringWriter (for whatever reasons), which would be a good and correct way?
Addition:
As it was already mentioned by both answers, I'll further go into the XML to DB problem.
When writing to the Database I got the following exception:
System.Data.SqlClient.SqlException: XML parsing: line 1, character 38, unable to switch the encoding
For string
<?xml version="1.0" encoding="utf-8"?><test/>
I took the string created from the XmlTextWriter and just put as xml there. This one did not work (neither with manual insertion into the DB).
Afterwards I tried manual insertion (just writing INSERT INTO ... ) with encoding="utf-16" which also failed. Removing the encoding totally worked then. After that result I switched back to the StringWriter code and voila - it worked.
Problem: I don't really understand why.
at Christian Hayter: With those tests I'm not sure that I have to use utf-16 to write to the DB. Wouldn't setting the encoding to UTF-16 (in the xml tag) work then?
One problem with
StringWriter
is that by default it doesn't let you set the encoding which it advertises - so you can end up with an XML document advertising its encoding as UTF-16, which means you need to encode it as UTF-16 if you write it to a file. I have a small class to help with that though:Or if you only need UTF-8 (which is all I often need):
As for why you couldn't save your XML to the database - you'll have to give us more details about what happened when you tried, if you want us to be able to diagnose/fix it.
<TL;DR> The problem is rather simple, actually: you are not matching the declared encoding (in the XML declaration) with the datatype of the input parameter. If you manually added
<?xml version="1.0" encoding="utf-8"?><test/>
to the string, then declaring theSqlParameter
to be of typeSqlDbType.Xml
orSqlDbType.NVarChar
would give you the "unable to switch the encoding" error. Then, when inserting manually via T-SQL, since you switched the declared encoding to beutf-16
, you were clearly inserting aVARCHAR
string (not prefixed with an upper-case "N", hence an 8-bit encoding, such as UTF-8) and not anNVARCHAR
string (prefixed with an upper-case "N", hence the 16-bit UTF-16 LE encoding).The fix should have been as simple as:
encoding="utf-8"
: simply don't add the XML declaration.encoding="utf-16"
: eitherSqlDbType.NVarChar
instead ofSqlDbType.VarChar
:-) (or possibly even switch to usingSqlDbType.Xml
)(Detailed response is below)
All of the answers here are over-complicated and unnecessary (regardless of the 121 and 184 up-votes for Christian's and Jon's answers, respectively). They might provide working code, but none of them actually answer the question. The issue is that nobody truly understood the question, which ultimately is about how the XML datatype in SQL Server works. Nothing against those two clearly intelligent people, but this question has little to nothing to do with serializing to XML. Saving XML data into SQL Server is much easier than what is being implied here.
It doesn't really matter how the XML is produced as long as you follow the rules of how to create XML data in SQL Server. I have a more thorough explanation (including working example code to illustrate the points outlined below) in an answer on this question: How to solve “unable to switch the encoding” error when inserting XML into SQL Server, but the basics are:
NVARCHAR(MAX)
orXML
/SqlDbType.NVarChar
(maxsize = -1) orSqlDbType.Xml
, or if using a string literal then it must be prefixed with an upper-case "N".VARCHAR(MAX)
/SqlDbType.VarChar
(maxsize = -1), or if using a string literal then it must not be prefixed with an upper-case "N".With the points outlined above in mind, and given that strings in .NET are always UTF-16 LE / UCS-2 LE (there is no difference between those in terms of encoding), we can answer your questions:
No, your
StringWriter
code appears to be just fine (at least I see no issues in my limited testing using the 2nd code block from the question).It isn't necessary to provide the XML declaration. When it is missing, the encoding is assumed to be UTF-16 LE if you pass the string into SQL Server as
NVARCHAR
(i.e.SqlDbType.NVarChar
) orXML
(i.e.SqlDbType.Xml
). The encoding is assumed to be the default 8-bit Code Page if passing in asVARCHAR
(i.e.SqlDbType.VarChar
). If you have any non-standard-ASCII characters (i.e. values 128 and above) and are passing in asVARCHAR
, then you will likely see "?" for BMP characters and "??" for Supplementary Characters as SQL Server will convert the UTF-16 string from .NET into an 8-bit string of the current Database's Code Page before converting it back into UTF-16 / UCS-2. But you shouldn't get any errors.On the other hand, if you do specify the XML declaration, then you must pass into SQL Server using the matching 8-bit or 16-bit datatype. So if you have a declaration stating that the encoding is either UCS-2 or UTF-16, then you must pass in as
SqlDbType.NVarChar
orSqlDbType.Xml
. Or, if you have a declaration stating that the encoding is one of the 8-bit options (i.e.UTF-8
,Windows-1252
,iso-8859-1
, etc), then you must pass in asSqlDbType.VarChar
. Failure to match the declared encoding with the proper 8 or 16 -bit SQL Server datatype will result in the "unable to switch the encoding" error that you were getting.For example, using your
StringWriter
-based serialization code, I simply printed the resulting string of the XML and used it in SSMS. As you can see below, the XML declaration is included (becauseStringWriter
does not have an option toOmitXmlDeclaration
likeXmlWriter
does), which poses no problem so long as you pass the string in as the correct SQL Server datatype:It may have been covered elsewhere but simply changing the encoding line of the XML source to 'utf-16' allows the XML to be inserted into a SQL Server 'xml'data type.
The result is all of the XML text is inserted into the 'xml' data type field but the 'header' line is removed. What you see in the resulting record is just
Using the serialization method described in the "Answered" entry is a way of including the original header in the target field but the result is that the remaining XML text is enclosed in an XML
<string></string>
tag.The table adapter in the code is a class automatically built using the Visual Studio 2013 "Add New Data Source: wizard. The five parameters to the Insert method map to fields in a SQL Server table.
When serialising an XML document to a .NET string, the encoding must be set to UTF-16. Strings are stored as UTF-16 internally, so this is the only encoding that makes sense. If you want to store data in a different encoding, you use a byte array instead.
SQL Server works on a similar principle; any string passed into an
xml
column must be encoded as UTF-16. SQL Server will reject any string where the XML declaration does not specify UTF-16. If the XML declaration is not present, then the XML standard requires that it default to UTF-8, so SQL Server will reject that as well.Bearing this in mind, here are some utility methods for doing the conversion.
First of all, beware of finding old examples. You've found one that uses
XmlTextWriter
, which is deprecated as of .NET 2.0.XmlWriter.Create
should be used instead.Here's an example of serializing an object into an XML column: