Powershell & XML - nested nodes & xsi:type= blah blah blah



我为一款名为《Space Engineers》的alpha/early access游戏运行一个专用服务器,偶尔他们会修补和破坏一些东西,我正试图创建一个powershell脚本来解决可能出现的问题。我对powershell很满意,从来没有搞砸过XML。

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

$power = Get-Content 'F:DedicatedServerDataDirSE Survival 2SavesVPS RC 1SANDBOX_0_0_0_.sbs' -raw
$power = $power -replace "<Enabled>true</Enabled>", "<Enabled>false</Enabled>"
$power | Out-File 'F:DedicatedServerDataDirSE Survival 2SavesVPS RC 1SANDBOX_0_0_0_.sbs' -Encoding ascii

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

[xml]$myXML = Get-Content 'F:DedicatedServerDataDirSE Survival 2SavesVPS RC 1SANDBOX_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:DedicatedServerDataDirSE Survival 2SavesVPS RC 1SANDBOX_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>

为true。

当前错误如下:

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)

为了能够在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:DedicatedServerDataDirSE Survival 2SavesVPS RC 1SANDBOX_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:DedicatedServerDataDirSE Survival 2SavesVPS RC 1SANDBOX_0_0_0_.sbs")

如果您计划进行更多的XML修改,您可能会发现使用原生XML技术更有用。因此,尽管它不能像您尝试的那样解决问题,但我想为您提供使用BaseX的解决方案(充分披露:我是项目团队的一员)。就我个人而言,我发现它比使用脚本解决方案简单得多。下载后,您可以使用以下命令运行BaseX:

basex -u -i input.xml query.xq

假设您的输入在input.xml文件中。-u标志表明对文件的更新应该传播回文件。查询存储在query中。Xq and有以下内容:

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

这个简单的声明将每个Enabled元素替换为等于false的文本,然后替换为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)看起来要简单得多。

相关内容

  • 没有找到相关文章

最新更新