我正在尝试创建一个带有根元素的 XML 文件:
<urn:Command complete="true" xmlns:urn="namespaceURI">
所以我有一个元素
Command
命名空间 namespaceURI
前缀 urn
最后是一个属性字符串,名称为 complete
,值为 true
,但没有命名空间。
我为此编写的代码返回:
<urn:Command xmlns:urn="namespaceURI" complete="true">
所以问题是我希望属性字符串位于 XML 文件中的命名空间定义之前,但我在这个网站上找不到类似的问题。
我尝试编写一个带有前缀和名称空间的
StartElement
,然后编写一个不带名称空间的 AttributeString
,这会返回根元素,其中先定义名称空间,后跟属性字符串。
我也尝试过仅定义一个开始元素,然后定义两个属性字符串,但后来我找不到将前缀写入开始元素的方法。
这是我的原始代码,它首先返回具有名称空间定义的根元素,然后返回属性定义:
`Dim Writer as System.Xml.XmlWriter;
dim writerSettings as System.Xml.XmlWriterSettings;
dim basePath as string;
dim source as string;
dim destination as string;
writerSettings = new System.Xml.XmlWriterSettings();
'writerSettings.ConformanceLevel= false;
'writerSettings.Encoding = new System.Text.UTF8Encoding(false);
writerSettings.OmitXmlDeclaration = false;
basePath = System.IO.Path.Combine("\\wnlcuieb502\WEI\Outbound","RolexSet");
source = System.IO.Path.Combine(basePath,"\\wnlcuieb502\WEI\Outbound","TEST.XML");
Writer = System.Xml.XmlWriter.Create(source,writerSettings);
Writer.WriteStartDocument();
Writer.WriteStartElement("urn","SetPackagingOrder","urn:laetus.com:ws:tts:mes");
Writer.WriteAttributeString("complete",null,"true");
Writer.WriteEndElement();
Writer.WriteEndDocument();
Writer.dispose();
try
destination = System.IO.Path.Combine(basePath,"TEST.XML");
while not System.IO.File.Exists(destination)
System.IO.File.Move(source,destination);
endwhile;
catch
LogError(Me.HierarchicalName + ": Could not move XML file: "+ "TEST.XML" +" from " + source + " to " + destination + ", Error: " + error.Message);
endtry;`
请注意开始标记或中属性规范的顺序 空元素标签并不重要。
命名空间声明就像属性(XML 建议中的 W3C 命名空间,第 3 声明命名空间部分),
[定义:命名空间(或更准确地说,命名空间绑定)是 使用一系列保留属性声明。此类属性的名称必须为 xmlns 或以 xmlns: 开头。这些 与任何其他 XML 属性一样,属性可以直接提供,也可以 默认。 ]
同样具有微不足道的排序。
因此,没有任何符合规范的 XML 工具或库会关心 XML 属性和 XML 命名空间声明的顺序,您也不应该。
这就是为什么 XML 库通常不提供约束属性排序的方法,而尝试这样做几乎总是表明您做错了什么。
XML 建议都认为属性排序和命名空间声明排序无关紧要,但如果您的应用程序不可避免地需要属性排序,请参阅 XML 规范化建议 或 规范 XML 推荐中的 有关属性处理的部分 。需要为数字签名建立词法相等/不相等(XML 签名语法和处理版本 1.1)就是这样一个例外。
另请参阅(但仅如果您绝对必须订购 XML 属性和命名空间声明):
.NET
Java
XSLT
多平台
saxon:attribute-order?
和 saxon:canonical?
XML 属性顺序确实很重要,例如在比较文档或生成哈希时(或将它们与其他加密操作结合使用时)。
至少有两种使用 C# 和 .NET 控制属性顺序的方法:
假设您有一个
XmlDocument
对象可供使用(例如,当 序列化您的 .NET 对象时,您可以获得一个对象),您可以通过删除所有属性并在中再次添加它们,以任何您想要的方式对所有元素的所有属性进行排序您希望它们的顺序:
public static void SortAllAttributes(XmlDocument xmlDocument)
{
foreach (XmlElement element in xmlDocument.SelectNodes("//*"))
{
var attributes = element.Attributes
.Cast<XmlAttribute>()
.ToArray();
element.Attributes.RemoveAll();
foreach (var attribute in attributes
.OrderBy(a => a.LocalName) // <-- custom sort order
.ThenBy(a => a.Name))
{
element.Attributes.Append(attribute);
}
}
}
假设您有一个
XmlDocument
对象可供使用(例如,当 序列化 .NET 对象时您可以获得一个),您可以生成该文档的 规范 XML 表示形式。作为该表示的一部分,所有属性都以特定方式排序(首先按命名空间 URI,然后按不带命名空间前缀的属性名称):
public static XmlDocument GetCanonicalXml(XmlDocument xmlDocument)
{
var xmlTransform = new XmlDsigC14NTransform();
xmlTransform.LoadInput(xmlDocument);
using var memoryStream = (MemoryStream)xmlTransform.GetOutput(typeof(Stream));
var canonicalXmlDocument = new XmlDocument();
canonicalXmlDocument.Load(memoryStream);
return canonicalXmlDocument;
}
规范 XML 有几个属性,例如即使对于空元素也强制开始/结束标记对,您可能想要补救。在将
XmlDocument
转换为规范 XML 后,您可以根据自己的喜好进行更改(这里,我们再次对空元素使用空元素语法(<foo />
而不是 <foo></foo>
):
public static void EnsureEmptyElementSyntaxUsage(XmlDocument xmlDocument)
{
foreach (XmlElement element in xmlDocument.SelectNodes("//*"))
{
if (!element.IsEmpty &&
string.IsNullOrWhiteSpace(element.InnerText) &&
string.IsNullOrWhiteSpace(element.InnerXml))
{
element.IsEmpty = true;
}
}
}
您可能还想输出启用换行符和缩进的文档(C14N(规范 XML)的默认 .NET 实现不输出任何换行符,也不缩进任何内容):
public static void WriteIndentedXmlDocument(string filePath, XmlDocument xmlDocument)
{
using var xmlTextWriter = new XmlTextWriter(filePath, Encoding.Default);
xmlTextWriter.Formatting = Formatting.Indented;
xmlDocument.WriteTo(xmlTextWriter);
}