Linq2SQL storing XElement to SQL Server database removes whitespaces

给你一囗甜甜゛ 提交于 2021-01-29 07:20:19

问题


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.


回答1:


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)




回答2:


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



来源:https://stackoverflow.com/questions/52498484/linq2sql-storing-xelement-to-sql-server-database-removes-whitespaces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!