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 access到via 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[]]
的元素.