【问题标题】:Powershell HasChildNodes is true for text?Powershell HasChildNodes 是否适用于文本?
【发布时间】:2018-11-13 03:05:22
【问题描述】:

在 Powershell 中使用 XML 对象时,如果元素只有文本数据而不是其他元素,HasChildNodes 是否应该返回 true?有没有办法识别下面没有任何实际元素的元素?

鉴于下面的示例,我希望元素 Parameter_Section 会为有孩子返回 true,但我不希望里面的元素在有数据时也返回 true。例如,SCM_Included、SendToApp 和 WF_Start 都返回 true,因为它们有文本。定义说“获取一个指示此节点是否有任何子节点的值”。这是否意味着文本或数据被视为子节点?

我正在解析一堆 InfoPath XML 文件,我希望能够忽略像 Parameter_Section 这样的父元素,它们仅用于组织(InfoPath 将它们称为“节”)实际存储数据的子元素(它们是InfoPath 中的字段)。

function ReadAllNodes ($node) {
foreach ($childnode in $node.ChildNodes)
{
    [string] $path = Get-XPath($childnode)
    [string] $nt = $childnode.NodeType
    [string] $hc = $childnode.HasChildNodes
    [string] $name = $childnode.Name
    [string] $val = $childnode.Value
    [string] $txt = $childnode.'#text'

    Write-Host (“Name={0}, path={1}, type={2}, hc={3}, val={4}, txt={5}” -f $name, $path, $nt, $hc,$val,$txt)
}

foreach ($cn in $childnode) {
    ReadAllNodes $cn
    }
}

$Xml = @"
<?xml version="1.0" encoding="utf-8"?>
<myFields>
    <Parameter_Section>
        <Approval_Mode />
        <SCM_Included>n</SCM_Included>
        <ApprovalCompleteDateTime />
        <ApprovalCompleteDateStr />
        <SendToApp>No</SendToApp>
        <WF_Start>0</WF_Start>
        <QuoteAttachCount>0</QuoteAttachCount>
        <TestEmail />
        <TestMessage />
        <IsCurrentUserRequestor>true</IsCurrentUserRequestor>
        <CanCurrentUserApprove>Approve</CanCurrentUserApprove>
    </Parameter_Section>
</myFields>
"@

$content = New-Object -TypeName XML
$content.LoadXml($Xml)
[System.Xml.XmlElement] $root = $content.get_DocumentElement()

ReadAllNodes $root





    Name=Parameter_Section, path=/myFields/Parameter_Section, type=Element, hc=True, val=, txt=
Name=Approval_Mode, path=/myFields/Parameter_Section/Approval_Mode, type=Element, hc=False, val=, txt=
Name=SCM_Included, path=/myFields/Parameter_Section/SCM_Included, type=Element, hc=True, val=, txt=n
Name=ApprovalCompleteDateTime, path=/myFields/Parameter_Section/ApprovalCompleteDateTime, type=Element, hc=False, val=, txt=
Name=ApprovalCompleteDateStr, path=/myFields/Parameter_Section/ApprovalCompleteDateStr, type=Element, hc=False, val=, txt=
Name=SendToApp, path=/myFields/Parameter_Section/SendToApp, type=Element, hc=True, val=, txt=No
Name=WF_Start, path=/myFields/Parameter_Section/WF_Start, type=Element, hc=True, val=, txt=0
Name=QuoteAttachCount, path=/myFields/Parameter_Section/QuoteAttachCount, type=Element, hc=True, val=, txt=0
Name=TestEmail, path=/myFields/Parameter_Section/TestEmail, type=Element, hc=False, val=, txt=
Name=TestMessage, path=/myFields/Parameter_Section/TestMessage, type=Element, hc=False, val=, txt=
Name=IsCurrentUserRequestor, path=/myFields/Parameter_Section/IsCurrentUserRequestor, type=Element, hc=True, val=, txt=true
Name=CanCurrentUserApprove, path=/myFields/Parameter_Section/CanCurrentUserApprove, type=Element, hc=True, val=, txt=Approve
Name=#text, path=/myFields/Parameter_Section/CanCurrentUserApprove/#text, type=Text, hc=False, val=Approve, txt=

【问题讨论】:

  • .NET 是真实的。如果某个项目不是nullstring.EmptyString0,则它的计算结果为true
  • @TheIncorrigible1:这不是问题的根源,但感谢您指出 any 非空字符串 in PowerShell 的“真实性”——甚至如果它恰好包含 literal 字符串 False。但是,请注意,这不是 .NET 本身的特性;例如,这种松散类型的 to-Boolean 转换在 C# 中根本不起作用。
  • @mklement0 它不仅仅适用于字符串,IIRC 同样适用于 C#(最近不需要写任何东西)。
  • @TheIncorrigible1:是的,in PowerShell to-Boolean coercion 适用于 any 数据类型,但关键是该逻辑取决于每个基于 .NET 的 语言,而不是 .NET(CLR)本身。而且,不,C#支持将任意类型强制转换为[bool]隐式

标签: xml powershell xml-parsing


【解决方案1】:

这是否意味着文本或数据被视为子节点?

是的,确实如此。

&lt;CanCurrentUserApprove&gt; 节点内的字符串“Approve”本身就是一个XmlText 节点。正如您所料,您可以通过ChildNodes 属性访问文本节点。

试试下面的例子:

$content.SelectNodes('//CanCurrentUserApprove')[0].ChildNodes[0]

【讨论】:

  • 我想我明白了这让我如此困惑的一个原因。我的循环只显示列表中最后一个的文本节点。如果循环显示所有其他元素的文本节点,它可能会更明显。我需要看看我是否可以修复那个循环。 Name=CanCurrentUserApprove, path=/myFields/Parameter_Section/CanCurrentUserApprove, type=Element, hc=True, val=, txt=Approve Name=#text, path=/myFields/Parameter_Section/CanCurrentUserApprove/#text, type=Text, hc=False, val=Approve, txt=
【解决方案2】:

听起来您想测试子 elements,而不是 nodes,因为看似包含在元素内的文本实际上是子 node类型为Text

虽然您可以单独检查 XML 元素[1] ,通过Select-Xml cmdlet 使用XPath 查询更容易

以下查找所有没有子元素的元素并返回.InnerText属性为非空的元素,暗示它们“包含文本”:

Select-Xml -XPath '//*[count(*)=0] and text()' -Content $Xml |
  Select-Object Node, @{ n='Text'; e={ $_.Node.InnerText } }

使用您的示例 XML,以上产生:

Node                   Text
----                   ----
SCM_Included           n
SendToApp              No
WF_Start               0
QuoteAttachCount       0
IsCurrentUserRequestor true
CanCurrentUserApprove  Approve
  • //*[count(*)=0] 仅匹配文档// 中任何位置没有元素 子元素(count(*)=0) 的任何名称(*) 的元素。

  • and text() 将匹配限制为.InnerText 属性值为非空的元素。

    • 注意:没有 element 子节点且具有非空 .InnerText 属性的元素节点不一定只有 单个 类型为 Text 的子节点;可能有多个子节点,包括TextEntityReferenceCDATASection 类型的任意组合,.InnerText 连接起来形成一个字符串。
  • Select-Object 调用构造自定义对象,每个对象的 .Node 属性包含一个匹配的 XML 元素,其 .Text 属性包含该元素的 .InnerText 值。


[1] 正如Mathias R. Jessen 指出的那样,稳健地检查给定元素是否存在元素子元素不存在实际上是不平凡的;在 PSv3+ 中,您可以使用以下内容:

$elem.ChildNodes.NodeType -notcontains 'Element'

另外检查这样的元素是否“包含文本”(具有 [combine to] 具有非空文本表示的非元素子节点):

$elem.ChildNodes.NodeType -notcontains 'Element' -and $elem.InnerText -ne '' 

你可以在末尾省略-ne '',因为PowerShell中的任何非空字符串都是“真实的”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多