使用 Windows PowerShell 构建易于使用的 XML 界面
Windows PowerShell 脚本语言不会你想要一个命令行工具,要做的一切 — — 和多得多 — — 它可能最终取代 VBScript 等技术。 Windows PowerShell 是关于什么好一般说明和使用它的基本操作,请参阅 bit.ly/LE4SU6 和 bit.ly/eBucBI。
Windows PowerShell 彻底与集成 Microsoft.net 框架,并因此深深地连接到 XML 数据交换使用结构化的文本的文件的当前国际标准。 有关 XML 的一般信息,请参阅 bit.ly/JHfzw。
本文介绍 Windows PowerShell 现状和操作,目标是创造一个相对简单的用户界面,用于读取和编辑 XML 文件的 XML 数据的能力。 这个想法是文件的使这更容易、 更方便使用的算法"理解"对于任何给定,即使没有悲伤的架构的同级和父-子关系。 我还会检查 Windows 窗体在 Windows PowerShell、 XPath 查询和其它相关的技术的使用。 拟议的应用程序可以消化的 XML 文件,并发出自己的 XPath 查询。
让我们看看如何可以分析 Windows PowerShell 中的任何 XML 文件并没有先进的技术技能的人可以使用的格式显示它。 图 1 显示的图形用户界面,您可以创建的类型的预览。
图 1 初步视图的图形用户界面
实现这一目标的关键是要使 Windows 电源外壳应用程序来分析和理解任何无人指导或预知其架构的 XML 文件。 经过研究,现有的技术,自动分析的 XML 文件,我决定开发解析引擎为此特定的目的,因为我是能够找到并没有完全解决,需要了解无人观看的 XML 文档。 目前,应用程序似乎普遍假定开发人员或用户是担当的元素、 属性和整体任何的架构给定的 XML 文档。 但一些 — — 可能是很多 — — 在真实世界的情况不属于这个范例。 例如,在许多数据消费者谁不是 XML 专家,但那些需要访问 XML 数据源的各种方案中,现存的范式的熟悉程度假设将失败。 同样,即使受过培训的专家或两个工作人员,如果一个组织面临的数百个或数千个不同结构化 XML 文件,人类处理可能很容易会不堪重负。
因此,所需要的是培训的一个解析的引擎,它会读取任何 XML 文件并发出 Xpath,普通用户而言,只有最少,可以使用搜索和编辑任何给定的 XML 文件。
XML 语法分析引擎
要符合 XML,文档的结束和开放括号必须匹配。 例如,如果一个元素 <ABC> 存在,那里还必须存在于同一文件的元素中有些迟点 </ABC>。 之间这些左、 右角括号,几乎任何东西都可以从理论上发生。 使用 XML 的这一基本原则,我带你如何自动构建一系列全面的 XPath 查询,这样即使相对缺乏经验的 XML 数据的消费者可以迅速把它们要用于查找和处理 XML 文件中的数据。
第一,建立一套的阵列,以容纳所有括号和右括号中的 XML 文件:
[int[]]$leading_brackets = @()
[int[]]$closing_brackets = @()
[string[]]$leading_value = @()
[string[]]$closing_value = @()
若要生成为强类型的 Windows PowerShell 中的未知大小数组,三个元素是必要的:[类型 []] 领先的一部分 ; $名称某部分 ; 与数组的未知的大小,() @ 符号。 Windows PowerShell 中的变量以美元作为其前导字符。 这些特定的阵列盖打开和关闭尖括号中的 XML 文档,以及与这些支架相关联的元素名称的字符串值的索引的位置。 例如,在 XML 中线 <PS1> 文本值 </PS1>、 领先的方括号的整数索引将是 0 和右括号的索引将是 15。 领导和收盘值在这种情况下会 PS1。
我们的目标 XML 进入内存,我们使用以下代码:
$xdoc = New-Object System.Xml.XmlDocument
$xdoc.Load("C:\temp\XMLSample.xml")
图 2 是正在使用的实际 XML 文件的局部视图。
图 2 局部视图的示例 XML 文件
<?xml version="1.0" encoding="utf-8"?>
<Sciences>
<Chemistry>
<Organic ID="C1" origination="Ancient Greece" age="2800 yrs">
<branch ID="C1a">
<size>300</size>
<price>1000</price>
<degree>easy></degree>
<origin>Athens</origin>
// Text for organic chem here
</branch>
<branch name="early" ID="C1b" source="Egypt" number="14">
<size>100</size>
<price>3000</price>
<degree>hard></degree>
<origin>Alexandria</origin>
// Text for original Egyptian science
</branch>
</Organic>
</Chemistry>
<Physics></Physics>
<Biology ID="B" origination="17th century" >
.
.
.
<Trees4a name="trees4a" age="40000000">
<type ID="Tda1">oakda</type>
<type ID="Tda2">elmda</type>
<type ID="Tda3">oakd3a</type>
</Trees4a>
</Plants>
</Biology>
</Sciences>
加载操作之后,此 XML 数据是在内存中。 为操作和分析 XML,使用现在 $xdoc 变量中实例化的文档对象模型 (但我还需要几个特殊的用途,如本文中稍后介绍指出使用 XPathNavigator 技术):
# Create an XPath navigator (comments in PowerShell code take the \"#\" leading character)
$nav = $xdoc.CreateNavigator()
Windows PowerShell 的最有趣的功能之一是内置函数或 cmdlet,称为 Get 成员,使您可以检查的方法和在 Windows PowerShell 权在 IDE 中的任何对象的属性,随着你的发展。 图 3 包括对公正的 $导航对象上创建的此 cmdlet 的调用和图 4 显示获取帮助调用时显示在 Windows PowerShell 综合编写脚本的环境 (ISE) 的结果。
图 3 Get 成员调用的结果
Get-Member -InputObject $nav
TypeName: System.Xml.DocumentXPathNavigator
Name MemberType Definition
---- ---------- ----------
AppendChild Method System.Xml.XmlWriter AppendChild(), System.V...
AppendChildElement Method System.Void AppendChildElement(string prefix...
CheckValidity Method bool CheckValidity(System.Xml.Schema.XmlSche...
Clone Method System.Xml.XPath.XPathNavigator Clone()
ComparePosition Method System.Xml.XmlNodeOrder ComparePosition(Syst...
Compile Method System.Xml.XPath.XPathExpression Compile(str...
CreateAttribute Method System.Void CreateAttribute(string prefix, s...
CreateAttributes Method System.Xml.XmlWriter CreateAttributes()
CreateNavigator Method System.Xml.XPath.XPathNavigator CreateNaviga...
DeleteRange Method System.Void DeleteRange(System.Xml.XPath.XPa...
DeleteSelf Method System.Void DeleteSelf()
Equals Method bool Equals(System.Object obj)
Evaluate Method System.Object Evaluate(string xpath), System...
GetAttribute Method string GetAttribute(string localName, string...
GetHashCode Method int GetHashCode()
TypeName: System.Xml.DocumentXPathNavigator
.
.
.
.
.
Value Property System.String Value {get;}
ValueAsBoolean Property System.Boolean ValueAsBoolean {get;}
ValueAsDateTime Property System.DateTime ValueAsDateTime {get;}
ValueAsDouble Property System.Double ValueAsDouble {get;}
ValueAsInt Property System.Int32 ValueAsInt {get;}
ValueAsLong Property System.Int64 ValueAsLong {get;}
ValueType Property System.Type ValueType {get;}
XmlLang Property System.String XmlLang {get;}
XmlType Property System.Xml.Schema.XmlSchemaType XmlType {get;}
图 4 结果的 Windows PowerShell 中获取帮助
虽然得到成员将经常把你正确的轨道上 Windows PowerShell 开发过程中,您还可以找到相关联的获取帮助 cmdlet 方便在此过程中。
如果您在命令行中,键入获取帮助 xml 中所示图 4,你会在这里显示的输出:
getName Category Synopsis
---- -------- --------
Export-Clixml Cmdlet Creates an XML-based representation of an object or...
Import-Clixml Cmdlet Imports a CLIXML file and creates corresponding obj...
ConvertTo-XML Cmdlet Creates an XML-based representation of an object.
Select-XML Cmdlet Finds text in an XML string or document.
about_format.ps1xml HelpFile The Format.ps1xml files in Windows PowerShell defin...
about_types.ps1xml HelpFile Explains how the Types.ps1xml files let you extend ...
如果您键入时获取帮助 about_types.ps1xml,您将看到所示的结果图 5。
图 5 获得与 Types.ps1xml 文件的帮助
TOPIC
about_Types.ps1xml
SHORT DESCRIPTION
Explains how the Types.ps1xml files let you extend the Microsoft .NET Framework types of the objects that are used in Windows PowerShell.
LONG DESCRIPTION
The Types.ps1xml file in the Windows PowerShell installation directory ($pshome) is an XML-based text file that lets you add properties and methods to the objects that are used in Windows PowerShell.
Windows PowerShell has a built-in Types.ps1xml file that adds several elements to the .NET Framework types, but you can create additional Types.ps1xml files to further extend the types.
SEE ALSO
about_Signing
Copy-Item
Get-Member
Update-TypeData
Windows PowerShell 集成系统的研究语法是全面和相对简单易用。 这是一个值得自己文章的主题。
要获取 XML 分析就绪状态,请使用 XpathNavigator 的选择方法:
$nav.Select("/") | % { $ouxml = $_.OuterXml }
此语句的第一部分,在调用。选择简单的 XPath 查询"/",给整个 XML 内容。 在第二部分,Windows PowerShell 符号后 |我谨此陈其对象管道做 foreach,由别名 %; Foreach,而不是别名,我可能已经过去。 循环里,建立工作 XML 字符串数据变量 $ouxml 从。在循环中正在处理的对象的 OuterXML 属性。 回指图 3。OuterXML 是一个 XPathNavigator 对象的属性。 此属性提供了一套完整的在 XML 文件中,这是为解析引擎正常工作所需的所有尖括号。
请注意对于一条管道穿过对象,$_ 的符号的特定实例,带点表示法,用于获取每个实例的属性和方法。 管道中的每个对象所致,或使用 $_ 符号引用。要获取的 $_ 对象的属性,请使用,例如,$_。名称 (如果名称的特定对象的成员属性)。 一切都通过一个 Windows PowerShell 管道是具有的属性和方法的对象。
最后的准备阶段之前解析是"合法化"的 XML 文本处理任何特殊的情况下,看起来就像 <ShortNode/>。 解析引擎而将看到另一种格式中同样的信息:<ShortNode> </ShortNode>。 下面的代码会启动此转换使用正则表达式和寻找匹配值:
$ms = $ouxml | select-string -pattern "<([a-zA-Z0-9]*)\b[^>]*/>" -allmatches
foreach($m in $ms.Matches){ ‘regularize’ to the longer format }
你现在可以看此应用程序的主要分析代码:解析引擎,将填充到前面列出的四个阵列。 图 6 显示测试文件,打开方括号内的代码。
图 6 测试开放方括号内的文件
# if you run out of “<” you’re done, so use the “$found_bracket” Boolean variable to test for presence of “<”
$found_bracket = $true
while($found_bracket -eq $true)
{
# Special case of first, or root element, of the XML document;
# here the script-level variable $ctr equals zero.
if($Script:ctr -eq 0)
{
#to handle the top-level root
$leading_brackets += $ouxml.IndexOf("<",$leading_brackets[$Script:ctr])
$leading_value += $ouxml.Substring(0,$ind)
$closing_brackets += $ouxml.IndexOf("</" + $leading_value[0].Substring(1))
$Script:ctr+=1
}
}
中的代码图 6 处理 XML 文档的根元素的特殊情况。 XML 的另一个基本规则是每个架构应包含单个根整套尖括号 ; 在这些封闭符号,XML 数据可以在结构符合相匹配的规则,刚才提到,就是为每个"<ABC>"存在的任何方式"< / ABC。"
请注意使用 + = 语法来添加到一个数组中的项或元素。 后来后被填充的元素,, 这类数组可以通过访问索引,如 $leading_brackets [3]。
在 IndexOf 参数中,注意的搜索,在方法中,第二个参数所代表的起始位置显示 $脚本的引用: ctr。 在 Windows PowerShell 中的变量具有不同的作用域,请按照创建它们是从。 因为这里的变量 $ctr 创建任何函数的范围之外,有否考虑脚本级和一个脚本级变量不能更改从函数内部,不涉及 $脚本。 内循环,而不是函数内部的 $脚本引用可能不是必需的但它是一个好的习惯,都要记住范围倍。
编码时,很好的线索,对范围的侵犯是一个变量,应更改的值,但不是 ; 通常,这是因为它超出了范围,并且需要据此前缀。
一旦处理的根元素,所有其他元素的处理一个别的块内:
else
{
# Check for more \"<\"
$check = $ouxml.IndexOf("<",$leading_brackets[$Script:ctr-1]+1)
if($check -eq - 1)
{
break
}
第一件要做的是要检查是否已到达文件的末尾 ; 该事件的标准是没有进一步的 < 符号。 前面的代码执行此操作。 如果没有更多 < 符号,被称为休息。
下一段代码的区分 < 例和 < / 例:
#eliminate "</" cases of "<"
if($ouxml.IndexOf("</",$leading_brackets[$Script:ctr-1]+1) -ne `
$ouxml.IndexOf("<",$leading_brackets[$Script:ctr-1]+1))
因为你想积累了所有开放尖括号,您想要只知道这些在现阶段的解析引擎的操作。 注意在比较中的"不平等"的 Windows PowerShell 语法:-ne。 有关营办商包括-eq,-lt 和-gt. 此外,在 Visual Basic 中 (但不同于 C#),您需要一个换行字符,该字符是背勾选符号 ('),继续的代码行。
如果测试成功,填充 $leading_brackets 数组的一个新的元素:
$leading_brackets += $ouxml.IndexOf("<",$leading_brackets[$Script:ctr-1]+1)
领先尖括号建立的最新小版本下, 一个任务是元素的分离的相关联的名称。 这项任务,注意初始通车后 < 和元素的名称,< ElementName,有一个或多个属性,或一个空格或括号内关闭,请在以下两种情况:
<ElementName attribute1="X" attribute2 = "Y">, or
<ElementName>
单独这两例用下面的代码,看看哪个先出现,一个空格或 > 符号:
$indx_space = $ouxml.IndexOf(" ",$leading_brackets[$Script:ctr])
$indx_close = $ouxml.IndexOf(">",$leading_brackets[$Script:ctr])
if($indx_space -lt $indx_close)
{
$indx_to_use = $indx_space
}
else
{
$indx_to_use = $indx_close
}
一旦建立了适当的结束点,聘请 $indx_to_use,以帮助隔离与现在处于焦点的领先的尖括号关联的字符串:
$leading_value += $ouxml.Substring($leading_brackets[$Script:ctr],($indx_to_use -
$leading_brackets[$Script:ctr]))
实际上,行距值是字符串开头 < 一个空间,或者结束或 >。
阶段被设置为捡起通过查找字符串结束尖括号的相关 < / ElementName:
$closing_brackets += $ouxml.IndexOf("</" + $leading_value[$Script:ctr].Substring(1),`
$leading_brackets[$Script:ctr]+1)
$Script:ctr+=1
最后,在案例之间的区别 < 和 < / 不是满足,递增的数组元素,并继续:
else
{
$leading_brackets[$Script:ctr-1] +=1
}
在此过程结束时,三个数组看上去像他们的数据的以下部分演示文稿:
$leading_brackets:
0 18 62 109 179 207 241 360 375 447 475 509 625 639 681 713 741 775 808 844 900 915 948 976 1012 1044 1077 1142 1154 1203 1292 1329 1344 1426 1475 1490 1616 1687 1701 1743 1810 1842 1890 1904 1941 1979 2031 2046 2085 2138 2153 2186 2235 2250 2315 2362 2378 2442 2476 2524 2539 2607 2643 2718
$leading_value:
<Sciences <Chemistry <Organic <branch <size <price <degree <origin <branch <size <price <degree <origin <Physics <Biology
$closing_brackets:
2718 1687 625 360 179 207 241 273 612 447 475 509 541 1142 900 713 741 775 808 844 882 1129 948 976 1012 1044 1077 1
建立节点的关系
现在它是解析引擎操作中的第二阶段的时间。 在此更复杂的阶段,$leading_brackets $closing_brackets 的序列建立父母子女及兄弟姐妹之间的关系的所有节点正在分析的 xml。 第一,确立的变量的数目如下:
# These variables will be used to build an automatic list of XPath queries
$xpaths = @()
$xpaths_sorted = @()
$xpath = @()
[string]$xpath2 = $null
下一步,第一次配对相邻的领导和右括号被固定:
$first_open = $leading_brackets[0]
$first_closed = $closing_brackets[0]
$second_open = $leading_brackets[1]
$second_closed = $closing_brackets[1]
并创建一些循环计数器:
$loop_ctr = 1
$loop_ctr3 = 0
该引擎将反复解析没有更多时间比第一阶段递增的 $ctr 变量的值时建筑的 $leading_brackets 和其他阵列 (以下如果语句是有关设立的 XML 节点结构的试金石):
if($second_closed -lt $first_closed)
如果 $second_closed 值小于 (-lt) $first_closed 的值,子关系建立:
<ElementOneName>text for this element
<ChildElementName>this one closes up before its parent does</ChildElementName>
</ElementOneName>
检测到的子节点,变量重置为打开关闭尖括号的下两个相邻对、 计数器递增和重要 $xpath 数组填充了一个新的元素:
$first_open = $leading_brackets[$loop_ctr]
$first_closed = $closing_brackets[$loop_ctr]
$second_open = $leading_brackets[$loop_ctr + 1]
$second_closed = $closing_brackets[$loop_ctr + 1]
$loop_ctr2 +=1
#if($loop_ctr2 -gt $depth){$loop_ctr2 -= 1}
$depth_trial+=1
$xpath += '/' + $leading_value[$loop_ctr-1]
$loop_ctr+=1
您现在已经达到解析引擎的关键处理阶段:怎样做时不再持有的父-子关系。
一个初步的问题是要消除重复解析引擎操作的过程中将会出现的。 要做到这一点,持有的 XPath 查询 (这是建造的解析引擎的密钥值) 的整个数组的变量是审查的元素的元素,以确保它不会已经包含 $xpath,而此时是 $xpath,设立了第八届中的代码行中的当前值列入新的建议的候选人图 7。
检查重复 Xpath 的图 7
$is_dupe = $false
foreach($xp in $xpaths)
{
$depth = $xpath.Length
$xp = $xp.Replace('/////','')
$xpath2 = $xpath
$xpath2 = $xpath2.Replace(" ","")
$xpath2 = $xpath2.Replace("<","")
if($xp -eq $xpath2)
{
$is_dupe = $true
#write-host 'DUPE!!!'
break
}
如果 $xpath 的当前值不是一份复本,它将追加到 $xpath 数组和 $xpath 重新成为一个空数组为其下一次的使用:
if($is_dupe -eq $false){$xpaths += ($xpath2 + '/////');}
$xpath = @()
$xpath2 = $null
解析引擎用于迭代继续通过 XML 的基本设备是重建阵列,每个 itera 突变。 若要实现此目的,第一步是创建新的临时数组对象,作为过渡的设备:
$replacement_array_values = @()
$replacement_array_opens = @()
$replacement_array_closes = @()
$finished = $false
$item_ct = 0
通过 $leading_value 数组和筛选掉不仅仅是当前的一个发动机循环:
foreach($item in $leading_value)
{
if($item -eq $leading_value[$loop_ctr - 1] -and $finished -eq $false)
{
$finished = $true
$item_ct+=1
continue #because this one should be filtered out
}
未筛选的值来填充到临时数组。 所有三个数组填充通过协会中,因为对应其索引的开启和关闭的尖括号阵列中的元素名称值的数组:
$replacement_array_values += $item
$replacement_array_opens += $leading_brackets[$item_ct]
$replacement_array_closes += $closing_brackets[$item_ct]
$item_ct +=1
当三个临时数组都完成后时,三个永久数组分配它们的新值:
$leading_value = $replacement_array_values
$opening_brackets = $replacement_array_opens
$closing_brackets = $replacement_array_closes
$loop_ctr+=1
解析引擎的第一阶段的下一次迭代是通过初始化尖括号相邻的第一次对准备好:
$first_open = $leading_brackets[0]
$first_closed = $closing_brackets[0]
$second_open = $leading_brackets[1]
$second_closed = $closing_brackets[1]
$loop_ctr = 1
$loop_ctr2 = 1
continue # Sends the engine back to the top of the loop
最后,要完成的 XPath 查询集,您生成的前面描述的过程可能不包括的短路径。 例如,在当前示例中,无额外最后这一步,XPath \Sciences\Chemistry 不会包括在内。 要测试的每个 XPath 查询每个短版本还存在,无重复的基本逻辑。 执行此步骤的函数是 AddMissingShortPaths,你可以看到这篇文章的代码下载内容中 (archive.msdn.microsoft.com/mag201208PowerShell)。
与手中的自动的 XPath 查询的所有,你准备好建立一个 Windows 窗体应用程序的用户。 在此期间,将只产生的 XPath 查询放入文件通过 Windows PowerShell C:\PowerShell\XPATHS.txt >> 输出的语法。
构建的 Windows 窗体应用程序
因为 Windows PowerShell 承载.net 库和类,可以编写下面的代码,从而使可供您应用 Windows 窗体和.net 的绘图类:
[void] [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
与地方这些基本的构造块,您可以生成窗体和控件,如下:
$form= New-Object Windows.Forms.Form
$form.Height = 1000
$form.Width = 1500
$drawinfo = 'System.Drawing'
$button_get_data = New-Object Windows.Forms.button
$button_get_data.Enabled = $false
$text_box = New-Object Windows.Forms.Textbox
$button_get_data.Text = "get data"
$button_get_data.add_Click({ShowDataFromXMLXPathFilter})
值得注意的,add_Click 将一个事件附加到控件的 Windows PowerShell 语法 — — 在这种情况下,将附加到该按钮的 click 事件的函数调用。 中的代码图 8 添加按钮和文本框。
图 8 添加按钮和文本框
$pointA = New-Object System.Drawing.Point
$listbox = New-Object Windows.Forms.Listbox
$form.Controls.Add($listbox)
$listbox.add_SelectedIndexChanged({PopulateTextBox})
$form.Controls.Add($button_get_data)
$form.Controls.Add($text_box)
$pointA.X = 800
$pointA.Y = 100
$button_get_data.Location = $pointA
$button_get_data.Width = 100
$button_get_data.Height = 50
$pointA.X = 400
$pointA.Y = 50
$text_box.Location = $pointA
$text_box.Width = 800
来填充您收集的 XPath 查询 $ 列表框,执行以下操作:
foreach($item in $xpaths)
{
$listbox.Items.Add($item.Substring(0,$item.Length - 5))
# Each row in the listbox should be separated by a blank row
$listbox.Items.Add(' ')
}
用户界面
图 9 由显示在左侧,其中之一由用户选择的工具生成的 XPath 查询显示用户界面。
图 9 选择 XPath 查询
最后一步,在用户按下 GetXMLData 按钮,并产生中所示的结果图 10。
图 10 结果窗口
那里你有 — — 一个简单的用户界面,用于读取和编辑 XML 文件,完全使用 Windows PowerShell 创建的。 在即将到来的 MSDN 杂志在线文章,我将继续在这个问题上向你展示如何处理 XML 文件,使用命名空间,以及说明如何使用此处显示允许编辑 XML 文件通过接口的技术。
Joe Leibowitz 是一位从事基础设施项目顾问。他可以在达到 joe.leibowitz@bridgewaresolutions.com。
衷心感谢以下技术专家对本文的审阅:托马斯 · Petchel