使用XPathDocument和LINQ to XML从XML文档获取名称空间

A fellow emailed me earlier asking how to get the namespaces from an XML document, but he was having trouble because the XML had some XML declarations like <?foo?>.

一位同事以前给我发了电子邮件,询问如何从XML文档获取名称空间,但他遇到了麻烦,因为XML具有一些<?foo?>之类的XML声明。

System.Xml方式 (A System.Xml Way)

XPathDocument has two cool methods, GetNamespace(localName) and GetNamespaceInScope, but they need a currentNode to work with.

XPathDocument有两个很酷的方法,即GetNamespace(localName)和GetNamespaceInScope,但是它们需要currentNode才能使用。

 string s = @"<?mso-infoPathSolution blah=""blah""?>
              <?mso-application progid=""InfoPath.Document"" versionProgid=""InfoPath.Document.2""?>
              <my:ICS203 xml:lang=""en-US"" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
           xmlns:my=""http://schemas.microsoft.com/office/infopath/2003/myXSD/2007-04-03T19:03:38""            
xmlns:xd=""http://schemas.microsoft.com/office/infopath/2003""> <my:HeaderData/></my:ICS203>"; XPathDocument x = new XPathDocument(new StringReader(s)); XPathNavigator foo = x.CreateNavigator(); foo.MoveToFollowing(XPathNodeType.Element); IDictionary<string, string> whatever = foo.GetNamespacesInScope(XmlNamespaceScope.All);

Once you're on the right note, in this case the first element, you can call GetNamespacesInScope and get a nice dictionary that has what you need inside it.

一旦记入正确的位置(在本例中为第一个元素),您就可以调用GetNamespacesInScope并获得一个不错的字典,其中包含您需要的内容。

namespaces

I really like the System.Xml APIs, they make me happy.

我真的很喜欢System.Xml API,它们使我感到高兴。

一种System.Xml + LINQ to XML Bridge的方法 (A System.Xml + LINQ to XML Bridge Methods Way)

How could we do this with the LINQ to XML namespace? Well, pretty much the same way with a much nicer first line (yes, this could be made smaller).

我们如何使用LINQ to XML名称空间来做到这一点? 好吧,第一行要好得多(是的,可以做得更小)。

 XDocument y = XDocument.Parse(s);
 XPathNavigator poo = y.CreateNavigator();
 poo.MoveToFollowing(XPathNodeType.Element);
 IDictionary<string, string>
   
   
    
     dude = foo.GetNamespacesInScope(XmlNamespaceScope.All);
   
   

Notice that the CreateNavigator hanging off of XDocument is actually an extension method that is there because we included the System.Xml.XPath namespace. There are a whole series of "bridge" methods that make moving between LINQ to XML APIs and System.Xml APIs seamless.

请注意,脱离XDocument的CreateNavigator实际上是那里的扩展方法,因为我们包含了System.Xml.XPath命名空间。 有一整套“桥接”方法,使LINQ to XML API和System.Xml API之间无缝切换。

image

See the (extension) there in the tooltip? There's also a different icon for extension methods when they show up in Intellisense. See the small blue-arrow added next to CreateNavigator?

看到工具提示中的(扩展名)吗? 扩展方法在Intellisense中显示时,还有一个不同的图标。 看到在CreateNavigator旁边添加的小蓝箭头吗?

image

These helper methods are "spot-welded" on to existing object instances when you import a namespace that defines them. They are also called 'mixins.'

当您导入定义对象的名称空间时,这些辅助方法将“点焊”到现有对象实例上。 它们也称为“ mixin”

纯粹的LINQ to XML方式 (A Purely LINQ to XML Way)

I also wanted to see how this could be done using LINQ to XML proper.

我还想看看如何使用LINQ to XML正确地做到这一点。

 Disclaimer: We are comparing Apples and Oranges here, so say, "wow that query is not as terse or compact as GetNamespacesInScope." We're comparing one layer of abstraction to a lower one. We could certainly make a mixin for XElements called GetNamespacesInScope and we'd be back where we started. The System.Xml method GetNamespacesInScope is hiding all the hard work.

免责声明:我们在这里比较Apple和Orange,所以说,“哇,查询不像GetNamespacesInScope那样简洁或紧凑。” 我们正在将一层抽象与一个较低的抽象进行比较。 我们当然可以为XElement创建一个名为GetNamespacesInScope的mixin,然后回到开始的地方。 System.Xml方法GetNamespacesInScope隐藏了所有艰苦的工作。

Big thanks to Ion Vasilian for setting me straight with this LINQ to XML Query!

非常感谢Ion Vasilian用这个LINQ to XML Query直接帮助我!

First we load the XML into an XDocument and ask for the attributes hanging off the root, but we just want namespace declarations.

首先,我们将XML加载到XDocument中,并要求悬挂在根目录之外的属性,但是我们只需要名称空间声明。

XDocument z = XDocument.Parse(s);
var result = z.Root.Attributes().
        Where(a => a.IsNamespaceDeclaration).
        GroupBy(a => a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName,
                a => XNamespace.Get(a.Value)).
        ToDictionary(g => g.Key, 
                     g => g.First());

Then we group them by namespace. Note the ternary operator ?: that returns "" for no namespace, else the namespaces local name as the key selector, and then gets an actual XNamespace.

然后,我们按名称空间对它们进行分组。 请注意,三元运算符?:不返回任何名称空间的“”,否则将名称空间的本地名称作为键选择器,然后获取实际的XNamespace。

Update: Ion wrote me and pointed out a mistake. I was calling z.Root.AncestorsAndSelf.Attributes, and I only needed to call z.Root.Attributes, or if I wanted to get all namespaces, z.Root.DescendantsAndSelf(). Thanks Ion! Ion says: "z.Root.AncestorsAndSelf() says: from the root element of the document find all ancestors and the element itself. In other words only the root element. If you want to find the in-scope namespace declarations for a given element ‘e’, then on that element you’ll do e.AncestorsAndSelf(). In other words, starting from the given element ‘e’ walking up the ancestors path and including the element itself look for attributes that are namespace declarations and build a dictionary … Note that the question for in-scope namespace declarations is answered by walking up a path in the tree and not by doing a full traversal of the tree starting from a given point (a la e.DescendantsAndSelf())."

更新:离子给我写信并指出一个错误。 我正在调用z.Root.AncestorsAndSelf.Attributes,只需要调用z.Root.Attributes,或者如果我想获取所有命名空间,则是z.Root.DescendantsAndSelf()。 谢谢离子! 离子说:“ z.Root.AncestorsAndSelf()说:从文档的根元素中找到所有祖先和元素本身。换句话说,只有根元素。如果要查找给定范围内的命名空间声明元素“ e”,然后在该元素上执行e.AncestorsAndSelf()。换句话说,从给定的元素“ e”开始,沿着祖先的路径前进,包括该元素本身,查找作为命名空间声明和构建的属性字典……请注意,范围内名称空间声明的问题是通过在树中的路径上行走而不是通过从给定点(例如e.DescendantsAndSelf())开始对树的完整遍历来回答的。”

It can be confusing to figure out the various types of these variables like "a" and "g". In this example, "a" is an XAttribute because the call to Attributes() is of IEnumerable<XAttribute> and g is of type IGrouping<string, XNamespace>, gleaned from the expression inside of GroupBy().

弄清楚这些变量的各种类型(如“ a”和“ g”)可能会造成混淆。 在此示例中,“ a”是XAttribute,因为对Attributes()的调用是IEnumerable <XAttribute>,而g的类型是IGrouping <string,XNamespace> (从GroupBy()内部的表达式中收集)。

We finish it off by taking the IGrouping and turning it into a dictionary with ToDictionary, selecting an appropriate key and the first At runtime "g" is an instance of System.Linq.Lookup<string, XNamespace>.Grouping which implements IGrouping, containing the namespace as an (and the only) element, subsequently retrieved with a call to First() and becomes the value side of the dictionary item.

通过使用IGrouping并将其转换为带有ToDictionary的字典,然后选择适当的键来完成它,第一个在运行时“ g”是System.Linq.Lookup <string,XNamespace> .Grouping的实例,它实现了IGrouping,包含命名空间作为(也是唯一的)元素,随后通过调用First()进行检索,并成为字典项的值侧。

image

Do also note one subtle detail. The System.Xml call to GetNamespacesInScope always includes the xmlns:xml namespace, declared implicitly. The LINQ query doesn't include this implicit namespace. Note also that these were sourced from XML Attributes and that the order of attributes is undefined (another way to say this is that attributes have no order.)

还要注意一个细微的细节。 对GetNamespacesInScope的System.Xml调用始终包含隐式声明的xmlns:xml命名空间。 LINQ查询不包含此隐式名称空间。 还要注意,这些是从XML属性获取的,并且属性的顺序是不确定的(另一种说法是属性没有顺序。)

翻译自: https://www.hanselman.com/blog/get-namespaces-from-an-xml-document-with-xpathdocument-and-linq-to-xml

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值