[英]c# XML Serialization: Order of namespace declarations
我有一個非常奇怪的情況。 我像這樣序列化我的命名空間:
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("xsd", "http://www.w3.org/2001/XMLSchema");
namespaces.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
serializer.Serialize(writer, config, namespaces);
在我的機器上,我得到以下 xml(我剛剛添加了換行符的一行):
<SystemConfiguration
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.schaeffler.com/sara/systemconfiguration/">
在構建服務器上,我使用相同的軟件:
<SystemConfiguration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.schaeffler.com/sara/systemconfiguration/">
您會看到 xsd 和 xsi 的順序交換了。 我檢查了序列化程序的實現,發現順序是用哈希表確定的,並且沒有 XmlSerializerNamespaces 接口來為命名空間實現我自己的序列化程序。
這是 XmlSerializationWriter 中的方法:
protected void WriteNamespaceDeclarations(XmlSerializerNamespaces xmlns)
{
if (xmlns != null)
{
foreach (DictionaryEntry dictionaryEntry in xmlns.Namespaces)
{
string localName = (string) dictionaryEntry.Key;
string ns = (string) dictionaryEntry.Value;
if (this.namespaces != null)
{
string str = this.namespaces.Namespaces[(object) localName] as string;
if (str != null && str != ns)
throw new InvalidOperationException(Res.GetString("XmlDuplicateNs", (object) localName, (object) ns));
}
string str1 = ns == null || ns.Length == 0 ? (string) null : this.Writer.LookupPrefix(ns);
if (str1 == null || str1 != localName)
this.WriteAttribute("xmlns", localName, (string) null, ns);
}
}
this.namespaces = (XmlSerializerNamespaces) null;
}
什么會導致哈希圖中命名空間的不同順序?
來自 msdn:
元素按照key的hash值進行排序,每個key在集合中只能存在一次。
DictionaryEntry
(一個struct
)的哈希值是從ValueType.GetHashCode()
提取的。 它很可能會返回一個無法確定的鍵 - 可能基於基礎參考值。 您需要做一些進一步的思考,以確定哈希是如何計算的。 它可能只是使用默認object
實現。
同樣來自 msdn:
哈希碼用於在基於哈希表的集合中進行高效的插入和查找。 哈希碼不是永久值。
我已經實現了一個自定義的 XmlWriter,以確保根元素中的命名空間在寫出之前進行排序:
/// <summary>
/// XmlWriter that ensures the namespace declarations in the root element are always sorted.
/// </summary>
class SortedNamespaceXmlWriter : XmlWriter
{
private readonly XmlWriter _baseWriter;
private readonly List<(string prefix, string uri)> _namespaces = new List<(string prefix, string uri)>();
private int _elementIndex;
private string _nsPrefix;
private bool _inXmlNsAttribute;
public SortedNamespaceXmlWriter(XmlWriter baseWriter) => _baseWriter = baseWriter;
public override void WriteStartElement(string prefix, string localName, string ns)
{
_elementIndex++;
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteStartElement(prefix, localName, ns);
}
public override void WriteStartAttribute(string prefix, string localName, string ns)
{
if (prefix == "xmlns")
{
_inXmlNsAttribute = true;
_nsPrefix = localName;
}
else
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteStartAttribute(prefix, localName, ns);
}
}
public override void WriteEndAttribute()
{
if (_inXmlNsAttribute)
_inXmlNsAttribute = false;
else
_baseWriter.WriteEndAttribute();
}
public override void WriteString(string text)
{
if (_inXmlNsAttribute)
_namespaces.Add((_nsPrefix, text));
else
_baseWriter.WriteString(text);
}
private void FlushRootElementAttributesIfNeeded()
{
if (_elementIndex != 1 || _namespaces.Count == 0)
return;
_namespaces.Sort((a, b) => StringComparer.Ordinal.Compare(a.prefix, b.prefix));
foreach (var (prefix, uri) in _namespaces)
{
_baseWriter.WriteStartAttribute("xmlns", prefix, null);
_baseWriter.WriteString(uri);
_baseWriter.WriteEndAttribute();
}
_namespaces.Clear();
}
public override WriteState WriteState => _baseWriter.WriteState;
public override void Flush() => _baseWriter.Flush();
public override string LookupPrefix(string ns) => _baseWriter.LookupPrefix(ns);
public override void WriteBase64(byte[] buffer, int index, int count)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteBase64(buffer, index, count);
}
public override void WriteCData(string text)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteCData(text);
}
public override void WriteCharEntity(char ch)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteCharEntity(ch);
}
public override void WriteChars(char[] buffer, int index, int count)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteChars(buffer, index, count);
}
public override void WriteComment(string text)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteComment(text);
}
public override void WriteDocType(string name, string pubid, string sysid, string subset) => _baseWriter.WriteDocType(name, pubid, sysid, subset);
public override void WriteEndDocument() => _baseWriter.WriteEndDocument();
public override void WriteEndElement()
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteEndElement();
}
public override void WriteEntityRef(string name)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteEntityRef(name);
}
public override void WriteFullEndElement()
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteFullEndElement();
}
public override void WriteProcessingInstruction(string name, string text)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteProcessingInstruction(name, text);
}
public override void WriteRaw(char[] buffer, int index, int count)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteRaw(buffer, index, count);
}
public override void WriteRaw(string data)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteRaw(data);
}
public override void WriteStartDocument() => _baseWriter.WriteStartDocument();
public override void WriteStartDocument(bool standalone) => _baseWriter.WriteStartDocument();
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteSurrogateCharEntity(lowChar, highChar);
}
public override void WriteWhitespace(string ws)
{
FlushRootElementAttributesIfNeeded();
_baseWriter.WriteWhitespace(ws);
}
}
我最終將生成的 XML 文檔加載到 XDocument 中並使用“ReplaceAttributes”方法交換屬性:
var xDoc = XDocument.Load(ms);
var originalAttributes = xDoc.Root.Attributes();
var orderedAttributes = originalAttributes.OrderBy(a => a.Name.ToString());
xDoc.Root.ReplaceAttributes(orderedAttributes);
在我的示例中,我按字母順序對屬性進行了排序,但您可以根據需要交換屬性。 它將遵循 IEnumerable 的順序。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.