如何将XML片段包含到XML文档中

时间:2015-06-02 08:37:40

标签: xml powershell doctype

我使用XML文件作为PowerShell脚本的配置文件,基于rkeithhill.wordpress.com上的博客文章。我的一些脚本共享相同的设置,我想从外部XML文件中包含这些设置。我所做的是:

<?xml version="1.0" standalone="no" ?>
<!DOCTYPE doc [
<!ENTITY shareddata SYSTEM "shared.xml">
]>
<configuration >
  <appSettings >
    &shareddata;
    <add key="motorcycle" value="Husqvarna TE610" />
    <add key="car" value="Ford Mondeo" />
  </appSettings>
</configuration>

并且shared.xml文件包含(仅限一行):

<add key="backhoe" value="Volvo BM 430 Hymas" />

使用以下代码段我加载XML并将其打印到控制台:

$config = [xml](get-content $path)
$text = $config.OuterXml
Write-Verbose "[LoadConfig] $text"

输出结果为:

VERBOSE: [LoadConfig] <?xml version="1.0" standalone="no"?>
<!DOCTYPE doc[
<!ENTITY shareddata SYSTEM "shared.xml">
]>
<configuration><appSettings>&shareddata;
<add key="motorcycle"    value="Husqvarna TE610" />
<add key="car" value="Ford Mondeo" /></appSettings> </configuration>

我加载XML的方式不包括shared.xml的内容,但它会解析DOCTYPE标记,因为如果我还原到无效文件,它将会失败。

那么,我怎样才能让PowerShell完全包含XML片段,以便loadconfig能够找到反铲的关键和价值呢?

我也试过

$Document = ( Select-Xml -Path myfile.xml -XPath / ).Node

但只要我在配置文件中有&shareddata;,它就不会加载任何内容:

2 个答案:

答案 0 :(得分:2)

我在C#中尝试过你的例子,它也没有用。这适用于C#,但是一旦您将shared.xml更改为包含TEST VALUE值(即只允许简单值,而不是xml),但它仍然无法在PS中运行:

<configuration >
  <appSettings >
    <test>&shareddata;</test>
    <add key="motorcycle" value="Husqvarna TE610" />
    <add key="car" value="Ford Mondeo" />
  </appSettings>
</configuration>

使用此代码:

$doc = new-object System.Xml.XmlDocument
$doc.Load("D:\config.xml")
$doc.configuration.appSettings.add

key        value
---        -----
motorcycle Husqvarna TE610
car        Ford Mondeo

C#中的相同例子:

XmlDocument doc = new XmlDocument();
doc.Load("d:\\config.xml");
var x = doc["configuration"]["appSettings"]["test"];
Console.WriteLine(x.InnerText);

输出

TEST VALUE

似乎Powershell设置了doc.XmlResolver = null;选项以防止安全相关问题,您可以在PS中看到$doc.XmlResolver没有返回任何内容。我没有成功通过$doc.XmlResolver = new-object System.Xml.XmlUrlResolver手动设置解析器。

立即

您可以使用变量替换来实现效果:

<configuration >
  <appSettings >
    $shared_xml
    <add key="motorcycle" value="Husqvarna TE610" />
    <add key="car" value="Ford Mondeo" />
  </appSettings>
</configuration>

$shared_xml = Get-Content 'D:\shared.xml'
[xml]$config = $ExecutionContext.InvokeCommand.ExpandString((gc $path))

如果在枚举块中枚举变量并加载适当的文件,则可以使这更容易。您可以事先不知道包含哪些文件。

立即

除非对解析器问题有一些正常的解决方案,否则这一切看起来都很麻烦。但是,保持配置为XML的想法在很多层面上都是错误的。为什么在使用powershell config时使用XML:

$config = @{ }
$config.motorcycle = 'Husqvarna TE610'
$config.car = 'Ford Mondeo'

$shared = @{}
$shared.backhoe = "Volvo BM 430 Hymas"

$config + $shared

输出

Name                           Value
----                           -----
backhoe                        Volvo BM 430 Hymas
car                            Ford Mondeo
motorcycle                     Husqvarna TE610

你可以跟随&#34;包括&#34;模式如:

<强> config.ps1

$config = @{ 
   motorcycle = 'Husqvarna TE610'
   car = 'Ford Mondeo'
}
. shared1.ps1
...
. sharedN.ps1

<强> sharedN.ps1

$config.sharedN_cfg1 = 1
$config.sharedN_cfg2 = 2

如果您真的需要XML,那么之后可以使用ConvertTo-CliXML和朋友。

如果你仍然想要追求你的方向,我建议你看一下XInclude(你需要第三方聚会来完成这项工作)。

答案 1 :(得分:1)

部分通知数据:https://www.red-gate.com/simple-talk/sysadmin/powershell/powershell-data-basics-xml/

在Powershell中,有多种方法可以加载XML文档,而且它们对外部实体的工作方式都不一样。

假设我们有一对简单的XML文档:

a.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE doc[
    <!ENTITY includeSnippet SYSTEM "../path/to/b.xml">
]>
<stuff>
    &includeSnippet;
    <something>foo</something>
</stuff>

b.xml

<somethingElse>bar</somethingElse>

在Powershell中,我们通常会做$myVar = [xml](Get-Content a.xml)这样的事情,我们希望事情有效 -

$myVar.stuff.somethingElse ?= 'bar'  --> false (was not loaded from b.xml)
$myVar.stuff.something ?= 'foo'  --> true (was in a.xml)

但是这并不像上面所述那样工作 - PowerShell的Get Content提供程序只是从磁盘读取文本 - [xml]强制转换(到XmlNode)将该文本解释为XML但它是&#39 ;太晚了&#34;加载其他资源。相反,我们以这种方式加载xml:

$myFile = resolve-path(".\a.xml")
$myDoc = new-object System.Xml.XmlDocument
$myDoc.load($myFile) 

实体包含工作 - 有点。我可能误解了Entity包含在此处的意图,但b.xml的内容并未直接替换到a.xml文档中,而是显示在新的包装标记中:

$myVar.stuff.includeSnippet.somethingElse == 'bar'
$myVar.stuff.something == 'foo'

我也考虑过使用XInclude(考虑到微软主张它,似乎有理由认为PS会支持它吗?) - 基本上将!Entity片换成参考:<xi:include href="b.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>

但是在这种情况下,上面的Powershell XML阅读技术并没有解决包含文件而我们什么也得不到。