views:

2352

answers:

5

Folks,

I have a webservice that returns data in ISO-8859-1 encoding - since it's not mine, I can't change that :-(

For auditing purposes, I'd like to store the resulting XML from these calls into a SQL Server 2005 table, in which I have a field of type "XML NULL".

From my C# code, I try to store this XML content into the XML field using a parametrized query, something like

SqlCommand _cmd = new SqlCommand("INSERT INTO dbo.AuditTable(XmlField) VALUES(@XmlContents)", _connection);

_cmd.Parameters.Add("@XmlContents", SqlDbType.Xml);
_cmd.Parameters["@XmlContents"].Value = (my XML response);

_cmd.ExecuteNonQuery();

Trouble is - when I run this code, I get back an error:

Msg 9402, Level 16, State 1, Line 1 XML parsing: line 1, character xy, unable to switch the encoding

?? I was trying to figure out where and how I could possibly "switch" the encoding - no luck so far. What does this really mean? I cannot store XML with ISO-8859-1 encoding in SQL Server 2005?? Or is there a trick to a) tell SQL Server 2005 to just accept this encoding, or b) to automagically convert the webservice response to UTF encoding before storing in SQL Server?

Thanks for any hints, pointers, tips! Marc

A: 

Hello Marc,

I found this on google. http://social.msdn.microsoft.com/forums/en-US/sqlxml/thread/d40ef582-4ffe-4f4b-b6b8-03c6c0ba1a32/

I think you can replace the line

_cmd.Parameters.Add("@XmlContents", SqlDbType.Xml);

with

_cmd.Parameters.Add("@XmlContents", System.Data.SqlTypes.SqlXml);
Nathan Koop
Hi Nathan, doesn't seem to work - I get a compile time error:Error 2 'System.Data.SqlTypes.SqlXml' is a 'type', which is not valid in the given contextIdeas??
marc_s
A: 

Could you possibly re-write the xml as unicode (perhaps to a MemoryStream) and send that? Note: if you are just storing the data, you can use varbinary(max) (and it will actually be quicker). This has no encoding difficulties, and will also allow you to audit any corrupt xml that you receive.

If you are querying the data as xml inside the database server then xml is obviously the way to go.

Marc Gravell
Thanks for the tip - but since I want to query and report on the data using XQuery, I do need to keep it as XML inside SQL Server 2005.
marc_s
Then perhaps look at simply re-writing the xml to chaneg the encoding before sending it to the server.
Marc Gravell
+2  A: 

You need to convert to utf-16

I'm not an expert on XML in SQL Server even though I use it, but we had the same problem last year and it was mis-match of the string datatype declared in SQL compared to the xml being sent.

gbn
Yes, it's a bit messy and involved, but it gets the job done - thanks for the tip and the link!
marc_s
A: 

Here's the solution I use:

And a slightly modified version of the code from above (I've tested it with a UTF8 file using SQL 2005):

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

...
using (SqlConnection connection = new SqlConnection("conn string"))
{
    connection.Open();
    string sql = "INSERT INTO mytable (xmlColumn) VALUES (@xmlData)";
    using (SqlCommand command = new SqlCommand(sql, connection))
    {
     // Swap round if the source file is unicode      
     string xml = File.ReadAllText(@"C:\myxml.xml");
     //string xml = File.ReadAllText(@"C:\myxml.xml", Encoding.Unicode);

     using (MemoryStream stream = new MemoryStream())
     {
      using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
      {
       writer.Write(xml);
       writer.Flush();
       stream.Position = 0;

       SqlParameter parameter = new SqlParameter("@xmlData", SqlDbType.Text);
       parameter.Value = new SqlXml(stream);
       command.Parameters.Add(parameter);
       command.ExecuteNonQuery();
      }
     }
    }
}
Chris S
Did you try this where the input file had ISO-8859-1 encoding? Also, you're missing "using" statements, so -1.
John Saunders
John, as I'm sure you know MemoryStream.Dispose does nothing so there's no gain there, flushing the writer (i.e. writer.Dispose) would leave the SqlCommand with a disposed Writer. If I performed the execute inside the Writer's using() then the Writer would not be flushed and so empty, which is my reasoning for why I've done it like above. The code expects a UTF16 file so it wouldn't work with IS0-8859 (which is UTF8 as far as I know).
Chris S
Missed the ISO-8859-1 part of the question, my mistake
Chris S
MemoryStream.Dispose may do nothing _today_. That's in implementation detail you should not depend on, ever. Don't end using until ExecuteNonQuery is finished, of course. And you need to set the stream position back to 0.
John Saunders
Ok fair enough, I've taken the advice and updated it. The code before was running (runs faster now strangely though).
Chris S
The reader SqlXml uses sets the stream position incidently, but that's along the lines of what you mentioned about MemoryStream.Dispose
Chris S
...and true to OOP best practices, it now ignores the framework implementations of IDisposable and disposes of it all. Hopefully it works as I have only compiled it on snipper compiler, I'm not able to test it on this pc.
Chris S
Much better. Just need one more "using" around the SqlCommand.
John Saunders
it didn't work, and now revision #74473 it does
Chris S
you're a strict task master John! added dispose for the DbCommand which I didn't spot
Chris S