我正在尝试通过他们的 API 从 WhatConverts 提取数据,然后将其上传到 SQL 表中。 当我更深入地研究底层数据的本质时,有几种数据类型作为对象返回;我只知道外部数据元素类型将是一个对象,但嵌套元素名称和嵌套对象数组中的类型是完全可变的。 返回的数据是json。
我的计划是将类型化数据导入到 SQL 中,对于嵌套数据,尝试将嵌套转换为 XML 并将其作为 XML 数据导入到 SQL 中,以便将来根据最终用户的要求进行处理。 在 SQL 导入之前,我将其从 Invoke-RestMethod 对象整理到 DataTable 中。 我无法处理嵌套数据,这就是我所在的位置:
#NOTE $importedData is an array of PSCustomObject returned from Invoke-RestMethod
$manipulatedData = New-Object System.Data.DataTable
$importedData | ForEach-Object {
$newRow = $manipulatedData.NewRow()
foreach ($prop in $_.PSObject.Properties) {
# nested object
if ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
$tempPropname = $prop.Name
if (-not ($manipulatedData.Columns.Contains($tempPropname))) {
$newCol = New-Object System.Data.DataColumn($tempPropname)
$manipulatedData.Columns.Add($newCol, "System.String" ) | Out-Null
}
#$prop | Select-Object Name,Value | ConvertTo-Xml -As String
$newRow.$tempPropname = $prop | Select-Object Name,Value | ConvertTo-Xml -As String
}
# primitive value
else {
$tempPropname = $prop.Name
if (-not ($manipulatedData.Columns.Contains(($tempPropname)))) {
$newCol = New-Object System.Data.DataColumn($tempPropname)
$manipulatedData.Columns.Add($newCol, $prop.TypeNameOfValue ) | Out-Null
}
if ($prop.Value -ne '{}') {
$newRow.$tempPropname = $prop.Value
}
}
}
$manipulatedData.Rows.Add($newRow)
}
对于这个练习,我相信我需要迭代每个嵌套的属性元素(我只关心 1 层深度),但后来我陷入困境,不得不从头开始构建 XML 文档,所以我改变了方向并尝试仅导出整个对象到 XML。
结果是垃圾,这是输出:
SetValueInvocationException:
Line |
90 | $newRow.$tempPropname = $prop | Select-Object Name,Value …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception setting "additional_fields": "Type of value has a mismatch with column typeCouldn't store <<?xml version="1.0" encoding="utf-8"?> <Objects> <Object Type="System.Management.Automation.PSCustomObject"> <Property Name="Name"
| Type="System.String">additional_fields</Property> <Property Name="Value" Type="System.Management.Automation.PSCustomObject"> <Property Type="System.String">@{Junk?=No; New Client?=Not Set; Service-Based?=Not Set}</Property> <Property Name="Junk?"
| Type="System.Management.Automation.PSNoteProperty">No</Property> <Property Name="New Client?" Type="System.Management.Automation.PSNoteProperty">Not Set</Property> <Property Name="Service-Based?" Type="System.Management.Automation.PSNoteProperty">Not
| Set</Property> </Property> </Object> </Objects>> in additional_fields Column. Expected type is Object[]."
您的症状意味着您的输入 JSON 格式不规则:其中一个列最初是基于第一个输入对象从array创建的,而后续输入对象中同一列(属性)的值恰好是单个
[pscustomobject]
实例。
这是重现您的问题的示例 JSON:
[
{
"Primitive": 42,
"Array": [ "foo", "bar" ]
},
{
"Primitive": 43,
"Array": {
"CustomObjectScalar": "foo"
}
}
]
通过上述输入,属性
Array
的列最初基于第一个输入对象创建为 [object[]]
(即作为元素不受类型约束的数组,这就是 ConvertFrom-Json
转换数组值的方式) JSON 属性),并且当尝试将 [string]
实例分配为为第二个输入对象创建的行的列值时,会出现您看到的 类型不匹配 错误。
这是解决问题的一个方法,但需满足以下限制:
如果遇到
[pscustomobject]
实例且列数据类型为:
[object[]]
,只需将实例的 XML 字符串表示形式包装在单元素数组中,即可避免类型不匹配。
[string]
,像以前一样按原样分配 XML 字符串表示形式。
否则,抛出(报告脚本终止错误)
在下面的代码中查找
# !!
注释。
#NOTE $importedData is an array of PSCustomObject returned from Invoke-RestMethod
$manipulatedData = New-Object System.Data.DataTable
$importedData | ForEach-Object {
$newRow = $manipulatedData.NewRow()
foreach ($prop in $_.PSObject.Properties) {
# nested object
if ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
$tempPropname = $prop.Name
$value = $prop | Select-Object Name, Value | ConvertTo-Xml -As String
if (-not ($manipulatedData.Columns.Contains($tempPropname))) {
$newCol = New-Object System.Data.DataColumn($tempPropname)
$manipulatedData.Columns.Add($newCol, 'System.String' ) | Out-Null
$dataType = [string] # !! Store the assigned type.
}
else {
# !! Determine the preexisting data type.
$dataType = $manipulatedData.Columns[$tempPropname].DataType
}
# !! Assign a column value based on the column's data type.
switch ($dataType) {
([Object[]]) {
$newRow.$tempPropname = @($value) # !! Wrap in single-element array
}
([string]) {
$newRow.$tempPropname = $value # !! Assign a string as-is, as before.
}
default {
# !! Report an error, if the column has any other data type.
throw "Unexpected preexisting data type of column ${tempPropertyName}: [$dataType]"
}
}
}
# primitive value
else {
$tempPropname = $prop.Name
if (-not ($manipulatedData.Columns.Contains(($tempPropname)))) {
$newCol = New-Object System.Data.DataColumn($tempPropname)
$manipulatedData.Columns.Add($newCol, $prop.TypeNameOfValue ) | Out-Null
}
if ($null -ne $prop.Value) {
$newRow.$tempPropname = $prop.Value
}
}
}
$manipulatedData.Rows.Add($newRow)
}