在PowerShell中,可以将XML元素的子 node 和属性作为属性进行访问:

([xml]"<book genre='novel'/>").Book.genre
novel

这是Property,不是NoteProperty:

([xml]"<book genre='novel'/>").Book | Get-Member genre
   TypeName: System.Xml.XmlElement

Name  MemberType Definition
----  ---------- ----------
genre Property   string genre {get;set;}

documentation人说:

属性是元素的属性,...

但是,在C#中,没有这样的属性:

XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel'/>");
// Console.WriteLine(doc.Book);  // Does not work
// Console.WriteLine(doc.DocumentElement.genre);  // Does not work
'XmlDocument' does not contain a definition for ...

这是PowerShell的一项功能吗?或者它是我错误使用的.NET功能?

用于实现这些属性的机制是什么?

推荐答案

tl;dr

  • 您看到的是PowerShell's adaptation of the XML DOM(.NET类型System.Xml.XmlDocument,可通过PowerShell中的type accelerator [xml]获得),其中presents XML child elements and attributes as properties,允许convenient accessvia dot notation.

    • Adaptation种.NET类型是PowerShell 100提供的几个功能之一.
  • 若要仅查看给定类型实例的.NET type-native成员,请将-View Base传递给Get-Member;例如:

    # -View Base excludes the *adapted* and possibly other ETS properties.
    [xml] '<foo/>' | Get-Member -View Base
    

Background information:

包含在System.Xml.XmlDocument个实例中的PowerShell decorates the object hierarchy个实例(例如,使用CAST [xml]创建):

  • 每级with properties named for the input document's specific elements and attributes[1];例如:

     ([xml] '<foo><bar>baz</bar></foo>').foo.bar # -> 'baz'
     ([xml] '<foo><bar id="1" /></foo>').foo.bar.id # -> '1'
    
  • turning multiple elements of the same name at a given hierarchy level implicitly into arrays(具体地说,属于类型[object[]]);例如:

     ([xml] '<foo><C>one</C><C>two</C></foo>').foo.C[1] # -> 'two'
    

如示例(以及问题中您自己的代码)所示,这允许access via convenient dot notation个.

注:

  • 如果使用点符号表示target an element that has at least one attribute and/or child elements,则element itself is returned(XmlElement实例);otherwise, it is the element's text content;例如:

    # The <bar> element's *text content* is returned, as a [string] ('baz'),
    # because it has only a text child node and no attributes
    ([xml] '<foo><bar>baz</bar></foo>').foo.bar
    
    # The <bar> element is returned as an XmlElement instance,
    # because it has an *attribute*.
    ([xml] '<foo><bar id="1">baz</bar></foo>').foo.bar
    
    # The <bar> element is returned as an XmlElement instance,
    # because it has *child elements*.
    ([xml] '<foo><bar><baz>quux</baz></bar></foo>').foo.bar
    
  • Updating XML documents via dot notation等于limited to simple, non-structural changes;上面的差值开始起作用:

    # OK - direct updating of the text content of a simple
    # element (no child nodes, no attributes).
    # $xml.foo.bar then yields 'new'
    ($xml = [xml] '<foo><bar>baz</bar></foo>').foo.bar = 'new'
    
    # OK - direct updating of the attribute of an
    # element.
    #  $xml.foo.bar.id then yields '2'
    ($xml = [xml] '<foo><bar id="1">baz</bar></foo>').foo.bar.id = 2
    
    # !! FAILS - because <bar> isn't a simple element in this case,
    # !! due to the presence of an *attribute*, you cannot directly assign new text content.
    # !! -> Error "Cannot set "bar" because only strings can be used as values to set XmlNode properties."
    ($xml = [xml] '<foo><bar id="1">baz</bar></foo>').foo.bar = 'new'
    
    # OK - assign to the type-native .InnerText property
    #  $xml.foo.bar.InnerText then yields 'new'
    ($xml = [xml] '<foo><bar id="1">baz</bar></foo>').foo.bar.InnerText = 'new'
    

点符号的downside个特点是,如果incidental个输入XML元素名恰好与intrinsic[System.Xml.XmlElement]属性名(对于single-element个属性)或内部[Array]个属性名(对于array值属性;[System.Object[]]派生自[Array])相同,则可以有name collisions个.

In the event of a name collision:如果被访问的属性包含:

  • a single child element([System.Xml.XmlElement]),incidental properties win;例如:

    # -> 'foo': i.e .the <foo> element's own name, using 
    # XmlElement's type-native .Name property.
    ([xml] '<foo><Bar>bar</Bar></foo>').foo.Name
    
    # -> !! 'bar': That is, the *adapted* .Name property - i.e. the 
    #    !! child element whose name happened to be "Name" takes precedences.
    ([xml] '<foo><Name>bar</Name></foo>').foo.Name
    
    • 获得对type-native个属性的可预测访问的workaround是直接调用底层属性访问器方法100:

      # -> 'foo', thanks to .get_Name() workaround
      ([xml] '<foo><Name>bar</Name></foo>').foo.get_Name()
      
      • alternative是使用intrinsic psbase property:

        # -> 'foo', thanks to .psbase workaround
        ([xml] '<foo><Name>bar</Name></foo>').foo.psbase.Name
        
  • an array of child elements,the 100 type's properties win..

    • Therefore, the following element names break dot notation with array-valued properties (obtained with reflection command
      Get-Member -InputObject 1, 2 -Type Properties, ParameterizedProperty):

      Item Count IsFixedSize IsReadOnly IsSynchronized Length LongLenth Rank SyncRoot
      
      • 例如,try 使用member-access enumeration来获取所有<bar>个元素的所有item个属性值:

        # !! Outputs the definition of the parameterized .Item property
        # !! of type [Array], 
        # !! *not* the values of the "Item" attributes of the <bar> child elements.
        ([xml] '<foo><bar item="one" /><bar item="two" /></foo>').foo.bar.item
        
      • workaround将使用explicit enumeration of array-valued properties,例如通过intrinsic .ForEach() method:

        # -> 'one', 'two'
        ([xml] '<foo><bar item="one" /><bar item="two" /></foo>').foo.bar.ForEach('item')
        

Dot nation is invariably case-insensitive - unlike XML itself,这是将元素和属性表示为properties的必然结果,因为PowerShell中的属性访问通常不区分大小写:

 # Dot notation: case-INSENSITIVE
 # -> 'bar', despite the case mismatch
 ([xml] '<FOO>bar</FOO>').foo
 # -> 'BAR', 'bar', i.e. *both* elements that match case-insensitively
 ([xml] '<root><FOO>BAR</FOO><foo>bar</foo></root>').root.foo

 # Type-native XML method: case-SENSITIVE
 # -> NO output, due to the case mismatch
 ([xml] '<FOO>bar</FOO>').SelectSingleNode('foo')

Dot notation ignores XML namespaces -不像XML原生功能:

 # Dot notation: Namespaces are ignored.
 # -> the <foo> element (as a whole, because it has attributes)
 #    despite not specifying the namespace.
 ([xml] '<ns1:foo xmlns:ns1="https://example.org">bar</ns1:foo>').foo

 # Type-native XML methods: Explicit namespace handling required:
 # -> No output
 ([xml] '<ns1:foo xmlns:ns1="https://example.org">bar</ns1:foo>')['foo']

 # -> OK - explicit use of the namespace prefix;
 ([xml] '<ns1:foo xmlns:ns1="https://example.org">bar</ns1:foo>')['ns1:foo']

 # -> No output; see below.
 ([xml] '<ns1:foo xmlns:ns1="https://example.org">bar</ns1:foo>').SelectSingleNode('foo')
  • For XPath queries with the type-native .SelectSingleNode() / .SelectNodes() methods, you need not only the use of namespace prefixes, but you first need to create a namespace manager that maps the prefixes used in queries to their namespace URIs - see this answer for an example of this technique.
    The same applies analogously to the Select-Xml cmdlet, which uses XPath queries too - see this answer for an example.

[1]如果给定元素同时具有属性and和同名元素,则PowerShell将报告both,作为array [object[]]的元素.

.net相关问答推荐

带有ASP.NET核心的Angular 项目模板.API试验

如何将 select 语句详细信息提取到不同的方法中仍然保持Eager 加载?

System.IO.Directory.Exists 在 LINQ 语句中失败,但在 foreach 循环中没有

如何在 ASP.NET Core MVC 中读取操作方法的属性?

为什么这两个比较有不同的结果?

移位比Java中的乘法和除法更快吗? .网?

无法加载文件或程序集 Microsoft.Extensions.DependencyInjection.Abstractions,版本 = 1.1.0.0

MongoDB C# 驱动程序 - 忽略绑定字段

AutoMapper 的替代品

比较 C# 中的字符串和对象

泛型方法是如何、何时、何地具体化的?

如何右对齐 DataGridView 列中的文本?

Mono 是树莓派

多个添加的实体可能具有相同的主键

强制 XmlSerializer 将 DateTime 序列化为 'YYYY-MM-DD hh:mm:ss'

如何在 Dapper.Net 中编写一对多查询?

C# - 在 WPF 应用程序中保存用户设置的方法?

通过继承扩展枚举

在foreach循环中修改列表的最佳方法是什么?

什么时候使用 Tuple 和 KeyValuePair 比较好?