Powershell& XML - 嵌套节点& xsi:type = blah blah blah

时间:2014-08-24 23:05:25

标签: xml nodes powershell-v3.0 game-development xsitype

我为一个名为Space Engineers的alpha /早期访问游戏运行一个专用服务器,偶尔会修补和破坏一些东西,我正在尝试创建一个PowerShell脚本来解决可能发生的问题。我对PowerShell没问题,从不搞乱XML。

第一步是禁用游戏中的所有对象,以便在服务器上减少计算量。像这样 - >

$power = Get-Content 'F:\DedicatedServer\DataDir\SE Survival 2\Saves\VPS RC 1\SANDBOX_0_0_0_.sbs' -raw
$power = $power -replace "<Enabled>true</Enabled>", "<Enabled>false</Enabled>"
$power | Out-File 'F:\DedicatedServer\DataDir\SE Survival 2\Saves\VPS RC 1\SANDBOX_0_0_0_.sbs' -Encoding ascii

第二步和第三步是将所有医疗室和电源(未显示)联机。这就是我遇到麻烦的地方。

[xml]$myXML = Get-Content 'F:\DedicatedServer\DataDir\SE Survival 2\Saves\VPS RC 1\SANDBOX_0_0_0_.sbs'

$ns = New-Object System.Xml.XmlNamespaceManager($myXML.NameTable)
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")

$node = $myXML.SelectNodes('//SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock‌​[@xsi:type="MyObjectBuilder_MedicalRoom"]/Enabled', $ns) | % {    
    #set all med bays to be enabled.

    $switch= Select-XML -XML $myXML -XPath $node
    $switch.Node.InnerText = $switch.Node.InnerText.Replace("false", "true")

    $myXML.Save('F:\DedicatedServer\DataDir\SE Survival 2\Saves\VPS RC 1\SANDBOX_0_0_0_.sbs')
}

以下是删除了非相关数据的XML文件的摘录 -

<?xml version="1.0"?>
<MyObjectBuilder_Sector xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Position>
  </Position>
  <SectorEvents> 
  </SectorEvents>
  <AppVersion>1044014</AppVersion>
  <SectorObjects>
    <MyObjectBuilder_EntityBase xsi:type="MyObjectBuilder_CubeGrid">
      <EntityId>72230476941025901</EntityId>
      <PersistentFlags>CastShadows InScene</PersistentFlags>
      <PositionAndOrientation>
      </PositionAndOrientation>
      <GridSizeEnum>Large</GridSizeEnum>
      <CubeBlocks>
        <MyObjectBuilder_CubeBlock xsi:type="MyObjectBuilder_CubeBlock">          
        </MyObjectBuilder_CubeBlock>
        <MyObjectBuilder_CubeBlock xsi:type="MyObjectBuilder_MedicalRoom">
          <SubtypeName>LargeMedicalRoom</SubtypeName>
          <EntityId>72107097601717796</EntityId>
          <Min x="4" y="1" z="-1" />
          <BlockOrientation Forward="Forward" Up="Up" />
          <ColorMaskHSV x="0" y="0.15" z="0.25" />
          <Owner>144233151425053409</Owner>
          <ShareMode>Faction</ShareMode>
          <CustomName>Lurch Enterprises</CustomName>
          <ShowOnHUD>false</ShowOnHUD>
          <Enabled>false</Enabled>
          <SteamUserId>0</SteamUserId>
        </MyObjectBuilder_CubeBlock>

XML文件未使用所需更改进行更新。

的每个实例都会改变
<MyObjectBuilder_CubeBlock xsi:type="MyObjectBuilder_MedicalRoom">
      <Enabled>false</Enabled>

为真。

目前的错误是:

Select-Xml : Cannot validate argument on parameter 'XPath'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:16 char:44
+     $switch= Select-XML -XML $myXML -XPath $node
+                                            ~~~~~
    + CategoryInfo          : InvalidData: (:) [Select-Xml], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.SelectXmlCommand

You cannot call a method on a null-valued expression.
At line:17 char:5
+     $switch.Node.InnerText = $switch.Node.InnerText.Replace("false", "true")
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

回答了!这是我想要的最终产品。

$filePath = '<your save file path here>\SANDBOX_0_0_0_.sbs'

#troubleshooting script
#switch EVERYTHING off
$power = Get-Content $filePath -raw
$power = $power -replace "<Enabled>true</Enabled>", "<Enabled>false</Enabled>"
$power | Out-File $filePath -Encoding ascii

#turn on medbays, reactors, batteries, solar panels ONLY
[xml]$myXML = Get-Content $filePath
$ns = New-Object System.Xml.XmlNamespaceManager($myXML.NameTable)
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
$nodes = $myXML.SelectNodes("//SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock[@xsi:type='MyObjectBuilder_MedicalRoom']/Enabled|//SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock[@xsi:type='MyObjectBuilder_Reactor']/Enabled|//SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock[@xsi:type='MyObjectBuilder_BatteryBlock']/Enabled|//SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock[@xsi:type='MyObjectBuilder_SolarPanel']/Enabled", $ns)
ForEach($node in $nodes)
{
$node.InnerText = "true"
}
$myXML.Save($filePath)

2 个答案:

答案 0 :(得分:0)

为了能够在XPath中使用xsi前缀,您需要将前缀到命名空间的URI映射注册到XmlNamespaceManager,然后将XmlNamespaceManager传递给SelectNodes()方法:

.....
$ns = New-Object System.Xml.XmlNamespaceManager($myXML.NameTable)
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
$node = $myXML.SelectNodes("your xpath that contains xsi prefix here", $ns)
.....

更新:

原来在你的XPath中[之前有隐藏的字符,这就是你得到“无效令牌”错误的原因。我不熟悉PowerShell特定的语法,但这对我来说很好:

[xml]$myXML = Get-Content "F:\DedicatedServer\DataDir\SE Survival 2\Saves\VPS RC 1\SANDBOX_0_0_0_.sbs"

$ns = New-Object System.Xml.XmlNamespaceManager($myXML.NameTable)
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
$nodes = $myXML.SelectNodes("//SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock[@xsi:type='MyObjectBuilder_MedicalRoom']/Enabled", $ns)
ForEach($node in $nodes)
{
    $node.InnerText = "true"
}
$myXML.Save("F:\DedicatedServer\DataDir\SE Survival 2\Saves\VPS RC 1\SANDBOX_0_0_0_.sbs")

答案 1 :(得分:0)

如果您计划进行更多XML修改,您可能会发现使用本机XML技术更有用。因此,虽然它没有解决问题,但您尝试使用BaseX为我提供解决方案(完全披露:我是项目团队的成员)。就个人而言,我发现它更简单,而不是使用脚本解决方案。下载后,您可以使用以下命令运行BaseX:

basex -u -i input.xml query.xq

这假设您的输入位于input.xml文件中。 -u标志指出应该将文件更新传播回文件。查询存储在query.xq中,并具有以下内容:

//SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock[@xsi:type="MyObjectBuilder_MedicalRoom"]/Enabled[. = "false"] ! (replace value of node . with 'true')

这个简单的状态是用文本等于false替换每个Enabled元素以替换为true。它使用简单的XQuery Update表达式。 !表示法只是一个方便的缩写,以下查询也是如此,但可能稍微更具可读性:

for $e in //SectorObjects/MyObjectBuilder_EntityBase/CubeBlocks/MyObjectBuilder_CubeBlock[@xsi:type="MyObjectBuilder_MedicalRoom"]/Enabled[. = "false"]
return replace value of node . with 'true'

对我来说,这两行(一个用于执行程序,一个用于XQuery)看起来更简单。