Is it allowed to System.Xml.XmlNamespaceManager.AddNamespace an empty namespace string?

huangapple go评论142阅读模式
英文:

Is it allowed to System.Xml.XmlNamespaceManager.AddNamespace an empty namespace string?

问题

We are processing an XML file format where the vendor changed the namespace of the elements between versions, but many of the elements and attributes are the same and we could re-use our small subset of XPath queries.

The old format looked like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
... interesting stuff here ...
  </PropertyGroup>
</Project>

while the new format looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
... interesting stuff here ...
  </PropertyGroup>
</Project>

Our existing code does this:

        XmlDocument doc = new XmlDocument();
        doc.Load(inputFile);
        XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
        nsMgr.AddNamespace("ms", "http://schemas.microsoft.com/developer/msbuild/2003");
        ...
        foreach (XmlNode xn in doc.DocumentElement.SelectNodes("//ms:PropertyGroup", nsMgr)) ...

Now, for the new format, I can do the following, and it still works with my VS2022 but I am not sure whether this is "legal", adding an empty namespace via AddNamespace?:

        XmlDocument doc = new XmlDocument();
        doc.Load(path);
        XmlElement root = doc.DocumentElement;
        XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
        if (!String.IsNullOrEmpty(root.NamespaceURI))
        {
            nsMgr.AddNamespace("ms", root.NamespaceURI);
        } else {
            // <Project Sdk="Microsoft.NET.Sdk">
            // Just register ms: as empty
            nsMgr.AddNamespace("ms", "");
        }

I found this other answer:

> Use of an empty string in a namespace declaration has a specific
> meaning in XML Namespaces 1.1: it turns it into an "undeclaration", so
> within its scope, the prefix "em" is not associated with any URI. But
> ...
> XML Namespaces 1.0 explicitly says (§2.2): The empty string, though it
> is a legal URI reference, cannot be used as a namespace name.

... but this is from the XML side of things, not the parser / parser helper side.

Q: Using C# System.Xml.*, how do I XPath-query two different XML files that have the same structure, but one declares a toplevel namespace and the other does not?

英文:

We are processing an XML file format where the vendor changed the namespace of the elements between versions, but many of the elements and attributes are the same and we could re-use our small subset of XPath queries.

The old format looked like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
... interesting stuff here ...
  </PropertyGroup>
</Project>

while the new format looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
... interesting stuff here ...
  </PropertyGroup>
</Project>

Our existing code does this:

        XmlDocument doc = new XmlDocument();
        doc.Load(inputFile);
        XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
        nsMgr.AddNamespace("ms", "http://schemas.microsoft.com/developer/msbuild/2003");
        ...
        foreach (XmlNode xn in doc.DocumentElement.SelectNodes("//ms:PropertyGroup", nsMgr)) ...

Now, for the new format, I can do the following, and it still works with my VS2022 but I am not sure whether this is "legal", adding an empty namespace via AddNamespace?:

		XmlDocument doc = new XmlDocument();
		doc.Load(path);
		XmlElement root = doc.DocumentElement;
		XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
		if (!String.IsNullOrEmpty(root.NamespaceURI))
		{
			nsMgr.AddNamespace("ms", root.NamespaceURI);
		} else {
			// <Project Sdk="Microsoft.NET.Sdk">
			// Just register ms: as empty
			nsMgr.AddNamespace("ms", "");
		}

I found this other answer:

> Use of an empty string in a namespace declaration has a specific
> meaning in XML Namespaces 1.1: it turns it into an "undeclaration", so
> within its scope, the prefix "em" is not associated with any URI. But
> ...
> XML Namespaces 1.0 explicitly says (§2.2): The empty string, though it
> is a legal URI reference, cannot be used as a namespace name.

... but this is from the XML side of things, not the parser / parser helper side.

Q: Using C# System.Xml.*, how do I XPath-query two different XML files that have the same structure, but one declares a toplevel namespace and the other does not?

答案1

得分: 2

如果您已经测试并确定在您正在使用的库System.Xml中,设置一个空命名空间可以按预期工作,那么请使用这种方法。

创建单元测试,如果库的将来版本更改行为将失败。但是由于当前行为在标准范围内,并且更改行为将是一个破坏性更改,所以很不可能库会在这方面改变行为。

MSBuild文件

很明显,您正在手动解析MSBuild文件。但没有解释其目的。

如果您正在通过XPath更改文件中的属性值,请不要这样做。定义属性,以便它们可以通过环境变量和/或命令行/property开关传递给MSBuild,以覆盖属性值。

如果您正在查询特定的设置或值,您可以通过自定义目标扩展项目以报告信息,并使用MSBuild运行该目标。您可以编写自定义目标一次,并通过使用Directory.Build.targets文件在项目之间共享它。

对于更复杂的情况,您可以考虑使用Microsoft.Build库。该库支持传统样式项目和SDK样式项目。

英文:

If you have tested and determined that with the library you are using, System.Xml, setting an empty namespace works as desired, then use that approach.

Create unit tests that will fail if a future version of the library changes behavior. But because the current behavior is within the standard and because changing the behavior would be a breaking change, it is very unlikely that the library would ever change behavior on this.

MSBuild Files

It is clear that you are hand parsing MSBuild files. To what end is not explained.

If you are changing property values within the file via XPath, don't. Define your properties so that they can be overridden and pass the property values to MSBuild via environment variables and/or the command line /property switch.

If you are querying for specific settings or values, you can extend your projects with a custom target that reports the information and use MSBuild to run that target. You can write your custom target once and share it across projects by using a Directory.Build.targets file.

For something more complex, you may consider using the Microsoft.Build library. The library supports both legacy style projects and SDK style projects.

答案2

得分: 0

虽然它更冗长,而且XPath看起来有点丑 - 你可以通过在*上进行通用匹配,然后使用谓词过滤器来限制元素的local-name(),使其不受命名空间的影响。

例如:

//*[local-name() = "PropertyGroup"]

而不是:

//ms:PropertyGroup

然后你就不需要担心注册命名空间前缀以及与其绑定的命名空间是什么。

英文:

Although it's more verbose, and the XPath is a bit ugly - you could make them namespace-agnostic by matching generically on * and then use a predicate filter to restrict by the local-name() of the element.

For example:

//*[local-name() = "PropertyGroup"]

instead of:

//ms:PropertyGroup

Then you don't need to worry about registering a namespace-prefix and what namespace is bound to it.

答案3

得分: 0

更好的做法是使用LINQ to XML API。它自2007年起在.NET Framework中可用。

以下代码是通用的。如果存在默认命名空间,它将通过GetDefaultNamespace()调用获取它。
如果不存在,则忽略它。

C#

void Main()
{
    const string filePath = @"e:\Temp\input.xml";
    XDocument xdoc = XDocument.Load(filePath);
    
    XNamespace ns = xdoc.Root.GetDefaultNamespace();
    
    foreach (XElement PropertyGroup in xdoc.Descendants(ns + "PropertyGroup"))
    {
        Console.WriteLine(PropertyGroup);        
    }
}
英文:

It is better to use LINQ to XML API. It is available in the .Net Framework since 2007.

The code below is generic. It will get a default namespace vis GetDefaultNamespace() call if it is there.
And ignore it if it is not there.

c#

void Main()
{
	const string filePath = @"e:\Temp\input.xml";
	XDocument xdoc = XDocument.Load(filePath);
	
	XNamespace ns = xdoc.Root.GetDefaultNamespace();
	
	foreach (XElement PropertyGroup in xdoc.Descendants(ns + "PropertyGroup"))
	{
		Console.WriteLine(PropertyGroup);		
	}
}

huangapple
  • 本文由 发表于 2023年6月12日 20:37:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76456748.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定