问题
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