简体   繁体   中英

Linq2SQL storing XElement to SQL Server database removes whitespaces

Can I force System.Data.Linq.DataContext to store XML into a SQL Server table's XML column preserving whitespace or is there any other way?

My test code is as follows:

        Guid MyNewQid = Guid.NewGuid();
        using (DataClassesDataContext context = DataClassesDataContext.CreateDataContext())
        {

            Guid myQID = Guid.Parse("{28da4eca-2c1a-4647-xxx-b398d1xxx013}");
            FromSwiftBck t2sData = context.GetTable<FromSwiftBck>().FirstOrDefault(o => o.QID == myQID);
            string messageLoaded = t2sData.CompleteMessage;
            int appHeaderLenght = messageLoaded.IndexOf("</AppHdr>") + 9;
            string strMsgHeader = messageLoaded.Substring(0, appHeaderLenght);
            string strMsgDocument = messageLoaded.Substring(appHeaderLenght);
            XElement serv = XElement.Parse(strMsgDocument, LoadOptions.PreserveWhitespace);

            SwiftOut swOut = new SwiftOut();
            swOut.QID = MyNewQid;
            swOut.InsertTime = DateTime.Now;
            swOut.Message = serv;
            swOut.Status = -100;
            swOut.Namespace = swOut.Message.GetDefaultNamespace().NamespaceName;
            swOut.MessageName = swOut.Message.Descendants().First().Name.LocalName;

            context.SwiftOuts.InsertOnSubmit(swOut);
            context.SubmitChanges();
        }
        using (DataClassesDataContext context = DataClassesDataContext.CreateDataContext())
        { 
            SwiftOut swOutStored = context.GetTable<SwiftOut>().FirstOrDefault(o => o.QID == MyNewQid);

            XElement storedXdoc = swOutStored.Message;

            MessageBox.Show(storedXdoc.ToString());

            context.SwiftOuts.DeleteOnSubmit(swOutStored);
            context.SubmitChanges();
        }
    }

When I read the data from the new context, I get the xml with whitespaces removed.

The XML datatype does NOT preserve your exact textual representation of the XML - it parses and tokenizes the XML for more optimal storage.

SQL Server does not guarantee that the XML returned is exactly the same (in terms of formatting, whitespaces etc.) as the input.

There's also no option or config setting to my knowledge to change this behavior.

See here for more details:

https://docs.microsoft.com/en-us/sql/relational-databases/xml/xml-data-type-and-columns-sql-server?view=sql-server-2017

Native storage as xml data type

The data is stored in an internal representation that preserves the XML content of the data. ..... The InfoSet content may not be an identical copy of the text XML, because the following information is not retained : insignificant white spaces, order of attributes, namespace prefixes, and XML declaration.

(emphasis was added by me)

Short version

You need to add the xml:space attribute. LoadOptions.PreserveWhitespace affects only how a string is parsed. It doesn't add the attribute. You should add :

serv.SetAttributeValue(XNamespace.Xml + "space", "preserve");

Long version

SQL Server does respect the xml:space attribute. This query :

create table #xmltest (field xml);

insert into #xmltest 
values
    ('<elem>         </elem>'),
    ('<elem xml:space="preserve">         </elem>');

select * from #xmltest;

Returns :

<elem />
<elem xml:space="preserve">         </elem>

The question's code doesn't specify that though. It parses a string into an XElement with the PreserveWhitespace but doesn't add that attribute. If the original string doesn't contain that attribute, XElement won't contain it either.

The code inserst the equivalent of the first value, which doesn'r require whitespace preservation.

For example :

XElement.Parse("<elem>   </elem>",LoadOptions.PreserveWhitespace).ToString();

Produces <elem> </elem> which doesn't specify that whitespace should be preserved. PreserveWhitespace in this case overrides the default behaviour which is to ignore whitespace.

Without PreserveWhitespace Parse would ignore whitespace and return <elem></elem> .

On the other hand :

XElement.Parse("<elem xml:space='preserve'>   </elem>").ToString()

Produces <elem xml:space="preserve"> </elem> . There's no need to use the PreserveWhitespace flag, XElement itself recognizes and respects that flag.

If the source XML string contains whitespace but doesn't contain the xml:space attribute, it has to be added so other classes and applications like SQL Server know they have to preserve whitespace. The following code :

var serv=XElement.Parse("<elem>   </elem>",LoadOptions.PreserveWhitespace);
serv.SetAttributeValue(XNamespace.Xml + "space", "preserve");
serv.ToString();

Will return <elem xml:space="preserve"> </elem> which will be recognized by SQL Server.

Bringing all this together

This LINQ to SQL code :

var serv=XElement.Parse("<elem>       </elem>",LoadOptions.PreserveWhitespace);
serv.SetAttributeValue(XNamespace.Xml + "space", "preserve");
Xmltests.InsertOnSubmit(new Xmltest{Field=serv});

Will insert :

<elem xml:space="preserve">       </elem>

Into the database

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM