我的情况非常奇怪。我像这样序列化我的命名空间:
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;
}
什么可能导致hashmap中命名空间的不同顺序?
答案 0 :(得分:3)
来自msdn:
元素根据键的哈希值排序,并且 每个密钥只能在集合中存在一次。
从DictionaryEntry
中提取struct
(a ValueType.GetHashCode()
)的哈希值。它可能返回一个不可确定的密钥 - 可能基于底层参考值。您需要进行一些进一步的反射,以确定如何计算哈希值。它可能只是使用默认的object
实现。
也来自msdn:
哈希码用于高效插入和查找 基于哈希表的集合。哈希码不是 永久价值。
答案 1 :(得分:0)
我实现了一个自定义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);
}
}
答案 2 :(得分:0)
我最终将生成的 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 的顺序。