基于其中的一个字段,在 Powershell 中比较 2 个对象的对象。 JSON 和 XML 填充的对象
Compare-Object in Powershell for 2 objects based on a field within. Objects populated by JSON and XML
抱歉我缺乏 powershell 知识,我一直在广泛寻找解决方案,因为我不是一个程序员。
背景:
我目前正在尝试标准化 Incapsula 中的一些站点设置。为此,我想维护一个带有规则的本地 XML 并使用一些 powershell 来下拉现有规则并将它们与现有规则进行比较以确保我不会加倍。我正在采用这种尝试仅将增量应用为的方法:
- 对于大多数设置,incapsula 不够智能,无法知道它已经存在
- 可以张贴到 API 的内容与 return 由 API
编辑的内容不同
示例:
下面是 API 将根据要求 return 的示例,这是 JSON 格式。
JSON FROM WEBSITE
{
"security": {
"waf": {
"rules": [{
"id": "api.threats.sql_injection",
"exceptions": [{
"values": [{
"urls": [{
"value": "google.com/thisurl",
"pattern": "EQUALS"
}],
"id": "api.rule_exception_type.url",
"name": "URL"
}],
"id": 256354634
}]
}, {
"id": "api.threats.cross_site_scripting",
"action": "api.threats.action.block_request",
"exceptions": [{
"values": [{
"urls": [{
"value": "google.com/anotherurl",
"pattern": "EQUALS"
}],
"id": "api.rule_exception_type.url",
"name": "URL"
}],
"id": 78908790780
}]
}]
}
}
}
这是 XML 的格式,其中包含我们的特定站点设置
OUR XML RULES
<waf>
<ruleset>
<rule>
<id>api.threats.sql_injection</id>
<exceptions>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/thisurl</url>
</exception>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/thisanotherurl</url>
</exception>
</exceptions>
</rule>
<rule>
<id>api.threats.cross_site_scripting</id>
<exceptions>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/anotherurl</url>
</exception>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/anotherurl2</url>
</exception>
</exceptions>
</rule>
</ruleset>
</waf>
我已经成功地能够使用比较对象命令将站点的其他设置与 XML 进行比较,但是它们的嵌套稍微简单一些,并没有给我带来太多麻烦。我坚持到底是逻辑问题还是比较对象的限制。下面是一个示例代码,它需要将提供的 json 和 xml 保存为堆栈。json/xml 在同一目录中,并且应该产生上述结果:
$existingWaf = Get-Content -Path stack.json | ConvertFrom-Json
[xml]$xmlFile = Get-Content -Path stack.xml
foreach ($rule in $xmlFile)
{
$ruleSet = $rule.waf.ruleset
}
foreach ($siteRule in $ExistingWaf.security.waf.rules)
{
foreach ($xmlRule in $ruleSet)
{
if ($xmlRule.rule.id -eq $siteRule.id)
{
write-output "yes"
$delta = Compare-Object -ReferenceObject @($siteRule.exceptions.values.urls.value | Select-Object) -DifferenceObject @($xmlRule.rule.exceptions.exception.url | Select-Object) -IncludeEqual | where {$xmlRule.rule.id -eq $siteRule.id}
$delta
}
}
}
这是可行的,但不是我想要的。我确实在对象之间进行了比较,但没有针对特定 ID 进行比较,它向我显示了以下结果:
InputObject SideIndicator
----------- -------------
google.com/thisurl ==
google.com/thisanotherurl =>
google.com/anotherurl =>
google.com/anotherurl2 =>
google.com/anotherurl ==
google.com/thisurl =>
google.com/thisanotherurl =>
google.com/anotherurl2 =>
之后我在哪里
InputObject SideIndicator
----------- -------------
google.com/thisurl ==
google.com/thisanotherurl =>
google.com/anotherurl ==
google.com/anotherurl2 =>
希望这是有道理的。
是否可以仅对 ID 匹配的值进行比较?
如果您还有其他问题,请告诉我。
谢谢。
问题是您的迭代逻辑,错误地处理了 XML 文档中的多个 规则在单次迭代中:
foreach ($xmlRule in $ruleSet)
没有枚举任何东西——而是处理了单个 <ruleset>
元素;要枚举 child <rule>
元素,您必须使用 $ruleSet.rule
.
$xmlRule.rule.exceptions.exception.url
然后 隐式地 迭代 all <rule>
child ren 并因此报告了 所有 的 URL,这解释了 Compare-Object
输出中的额外行。
这是您的代码的简化版、带注释的版本:
$existingWaf = Get-Content -LiteralPath stack.json | ConvertFrom-Json
$xmlFile = [xml] (Get-Content -raw -LiteralPath stack.xml )
# No need for a loop; $xmlFile is a single [System.Xml.XmlDocument] instance.
$ruleSet = $xmlFile.waf.ruleset
foreach ($siteRule in $ExistingWaf.security.waf.rules)
{
# !! Note the addition of `.rule`, which ensures that the rules
# !! are enumerated *one by one*.
foreach ($xmlRule in $ruleSet.rule)
{
if ($xmlRule.id -eq $siteRule.id)
{
# !! Note: `$xmlRule` is now a single, rule, therefore:
# `$xmlRule.rule.[...]-> `$xmlRule.[...]`
# Also note that neither @(...) nor Select-Object are needed, and
# the `| where ...` (Where-Object) is not needed.
Compare-Object -ReferenceObject $siteRule.exceptions.values.urls.value `
-DifferenceObject $xmlRule.exceptions.exception.url -IncludeEqual
}
}
}
关于您的代码的其他观察结果:
不需要保证传递给Compare-Object
的操作数是数组,所以不需要用数组[=101]包裹起来=]运算符@(...)
。 Compare-Object
可以很好地处理标量操作数。
... | Select-Object
是虚拟的 no-op - 输入 object 通过 传递 [1]
... | Where-Object {$xmlRule.rule.id -eq $siteRule.id}
是没有意义的,因为它复制了封闭的 foreach
循环的条件。
- 一般来说,因为您没有通过自动变量
$_
引用手头的管道输入 object,您的 Where-Object
过滤器是 static 并将匹配 all 输入 objects (如您的情况)或 none.
[1]是一个微妙的、不可见的副作用,通常不会产生什么影响: Select-Object
在输入 object 周围添加了一个不可见的 [psobject]
包装器,这在极少数情况下会导致以后出现不同的行为 -
见 this GitHub issue.
抱歉我缺乏 powershell 知识,我一直在广泛寻找解决方案,因为我不是一个程序员。
背景:
我目前正在尝试标准化 Incapsula 中的一些站点设置。为此,我想维护一个带有规则的本地 XML 并使用一些 powershell 来下拉现有规则并将它们与现有规则进行比较以确保我不会加倍。我正在采用这种尝试仅将增量应用为的方法:
- 对于大多数设置,incapsula 不够智能,无法知道它已经存在
- 可以张贴到 API 的内容与 return 由 API 编辑的内容不同
示例:
下面是 API 将根据要求 return 的示例,这是 JSON 格式。
JSON FROM WEBSITE
{
"security": {
"waf": {
"rules": [{
"id": "api.threats.sql_injection",
"exceptions": [{
"values": [{
"urls": [{
"value": "google.com/thisurl",
"pattern": "EQUALS"
}],
"id": "api.rule_exception_type.url",
"name": "URL"
}],
"id": 256354634
}]
}, {
"id": "api.threats.cross_site_scripting",
"action": "api.threats.action.block_request",
"exceptions": [{
"values": [{
"urls": [{
"value": "google.com/anotherurl",
"pattern": "EQUALS"
}],
"id": "api.rule_exception_type.url",
"name": "URL"
}],
"id": 78908790780
}]
}]
}
}
}
这是 XML 的格式,其中包含我们的特定站点设置
OUR XML RULES
<waf>
<ruleset>
<rule>
<id>api.threats.sql_injection</id>
<exceptions>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/thisurl</url>
</exception>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/thisanotherurl</url>
</exception>
</exceptions>
</rule>
<rule>
<id>api.threats.cross_site_scripting</id>
<exceptions>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/anotherurl</url>
</exception>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/anotherurl2</url>
</exception>
</exceptions>
</rule>
</ruleset>
</waf>
我已经成功地能够使用比较对象命令将站点的其他设置与 XML 进行比较,但是它们的嵌套稍微简单一些,并没有给我带来太多麻烦。我坚持到底是逻辑问题还是比较对象的限制。下面是一个示例代码,它需要将提供的 json 和 xml 保存为堆栈。json/xml 在同一目录中,并且应该产生上述结果:
$existingWaf = Get-Content -Path stack.json | ConvertFrom-Json
[xml]$xmlFile = Get-Content -Path stack.xml
foreach ($rule in $xmlFile)
{
$ruleSet = $rule.waf.ruleset
}
foreach ($siteRule in $ExistingWaf.security.waf.rules)
{
foreach ($xmlRule in $ruleSet)
{
if ($xmlRule.rule.id -eq $siteRule.id)
{
write-output "yes"
$delta = Compare-Object -ReferenceObject @($siteRule.exceptions.values.urls.value | Select-Object) -DifferenceObject @($xmlRule.rule.exceptions.exception.url | Select-Object) -IncludeEqual | where {$xmlRule.rule.id -eq $siteRule.id}
$delta
}
}
}
这是可行的,但不是我想要的。我确实在对象之间进行了比较,但没有针对特定 ID 进行比较,它向我显示了以下结果:
InputObject SideIndicator
----------- -------------
google.com/thisurl ==
google.com/thisanotherurl =>
google.com/anotherurl =>
google.com/anotherurl2 =>
google.com/anotherurl ==
google.com/thisurl =>
google.com/thisanotherurl =>
google.com/anotherurl2 =>
之后我在哪里
InputObject SideIndicator
----------- -------------
google.com/thisurl ==
google.com/thisanotherurl =>
google.com/anotherurl ==
google.com/anotherurl2 =>
希望这是有道理的。
是否可以仅对 ID 匹配的值进行比较?
如果您还有其他问题,请告诉我。
谢谢。
问题是您的迭代逻辑,错误地处理了 XML 文档中的多个 规则在单次迭代中:
foreach ($xmlRule in $ruleSet)
没有枚举任何东西——而是处理了单个<ruleset>
元素;要枚举 child<rule>
元素,您必须使用$ruleSet.rule
.$xmlRule.rule.exceptions.exception.url
然后 隐式地 迭代 all<rule>
child ren 并因此报告了 所有 的 URL,这解释了Compare-Object
输出中的额外行。
这是您的代码的简化版、带注释的版本:
$existingWaf = Get-Content -LiteralPath stack.json | ConvertFrom-Json
$xmlFile = [xml] (Get-Content -raw -LiteralPath stack.xml )
# No need for a loop; $xmlFile is a single [System.Xml.XmlDocument] instance.
$ruleSet = $xmlFile.waf.ruleset
foreach ($siteRule in $ExistingWaf.security.waf.rules)
{
# !! Note the addition of `.rule`, which ensures that the rules
# !! are enumerated *one by one*.
foreach ($xmlRule in $ruleSet.rule)
{
if ($xmlRule.id -eq $siteRule.id)
{
# !! Note: `$xmlRule` is now a single, rule, therefore:
# `$xmlRule.rule.[...]-> `$xmlRule.[...]`
# Also note that neither @(...) nor Select-Object are needed, and
# the `| where ...` (Where-Object) is not needed.
Compare-Object -ReferenceObject $siteRule.exceptions.values.urls.value `
-DifferenceObject $xmlRule.exceptions.exception.url -IncludeEqual
}
}
}
关于您的代码的其他观察结果:
不需要保证传递给
Compare-Object
的操作数是数组,所以不需要用数组[=101]包裹起来=]运算符@(...)
。Compare-Object
可以很好地处理标量操作数。... | Select-Object
是虚拟的 no-op - 输入 object 通过 传递 [1]... | Where-Object {$xmlRule.rule.id -eq $siteRule.id}
是没有意义的,因为它复制了封闭的foreach
循环的条件。- 一般来说,因为您没有通过自动变量
$_
引用手头的管道输入 object,您的Where-Object
过滤器是 static 并将匹配 all 输入 objects (如您的情况)或 none.
- 一般来说,因为您没有通过自动变量
[1]是一个微妙的、不可见的副作用,通常不会产生什么影响: Select-Object
在输入 object 周围添加了一个不可见的 [psobject]
包装器,这在极少数情况下会导致以后出现不同的行为 -
见 this GitHub issue.