ConvertFrom-Jsonの出力はPSCustomObject

タイトル通り。
ハッシュテーブルだと思い込んでちょっと悩んだ。

# JSONから変換したオブジェクトにAddできない？

JSONに要素を追加する際、文字列を直接切り張りしていたらキリストがお嘆きになろうから、オブジェクトに変換して操作しようと思った。
が、エラーになる。

```powershell:JSONに要素を追加したい
$JSON = @'
{
  "alpha": "One",
  "beta" : "Two",
  "gamma": "Three",
  "delta": [ "Four", "Five" ]
}
'@

$Object = $JSON | ConvertFrom-Json

$Object.Add("nu","Six") #エラーになる
```

```console:
[System.Management.Automation.PSCustomObject] に 'Add' という
名前のメソッドが含まれないため、メソッドの呼び出しに失敗しました。
```

おや？エラーを見るとPSCustomObjectとある。
あれ？Hashtableじゃない…。

# ConvertFrom-JsonはPSCustomObjectを出力する

何だこれと調べたところ、公式ドキュメントにしっかり書いてあった。
[ConvertFrom-Json(公式ドキュメント)](https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.utility/convertfrom-json?view=powershell-5.1)
>ConvertFrom-Jsonコマンドレットは、JavaScript Object Notation（JSON）形式の文字列を、JSON文字列の各フィールドのプロパティを持つカスタムPSCustomObjectオブジェクトに変換します

```powershell:Convert前後での変化
$Array = @{
  alpha = "One"
  beta = "Two"
  gamma = "Three"
  delta = @("Four","Five")
}

$Array.GetType()

# JSONにConvertして戻すとPSCustomObjectで出てくる
$Array_AfterConvert = $Array | ConvertTo-Json | ConvertFrom-Json

$Array_AfterConvert.GetType()
```

```console
IsPublic IsSerial Name           BaseType
-------- -------- ----           --------
True     True     Hashtable      System.Object
True     False    PSCustomObject System.Object
```

仕様です。
思い込んでないで疑え、迷うより公式読めって話ですね。

これで終わりだと寂しいので当初の目的である要素追加や型変換などのことを以下に。

# PSCustomObjectに要素を追加する

`Add-Member`を使う。
[Add-Member(公式ドキュメント)](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-member?view=powershell-5.1)

```powershell:Add-Memberで要素追加
$JSON =@'
{
  "alpha": "One",
  "beta" : "Two",
  "gamma": "Three",
  "delta": ["Four","Five"]
}
'@

$Object = $JSON | ConvertFrom-Json

Write-Host "Add前"
$Object | Format-List

$Object | Add-Member -NotePropertyName "nu" -NotePropertyValue "Six"

Write-Host "Add後"
$Object | Format-List

Write-Host "Get-Member"
$Object | Get-Member
```

```console
Add前

alpha : One
beta  : Two
gamma : Three
delta : {Four, Five}

Add後

alpha : One
beta  : Two
gamma : Three
delta : {Four, Five}
nu    : Six

Get-Member

   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition                    
----        ----------   ----------                    
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()             
GetType     Method       type GetType()                
ToString    Method       string ToString()             
alpha       NoteProperty string alpha=One              
beta        NoteProperty string beta=Two               
delta       NoteProperty Object[] delta=System.Object[]
gamma       NoteProperty string gamma=Three            
nu          NoteProperty string nu=Six   
```



# PSCustomObjectからHashtableに変換したい

コマンドレット一発変換とはいかない。
先にハッシュテーブルを作っておいて、PSCustomObjectから読み込んで代入するといいようだ。ジャグ配列の時は注意な！な話もあってまだ使ってない。

[PSCustomObject について知りたかったことのすべて(MSのドキュメント)](https://docs.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7)
[stackoverflow:Create Hashtable from JSON](https://stackoverflow.com/questions/40495248/create-hashtable-from-json)

# AsHashtableパラメータ指定して変換(PowerShell 7)

PowerShell 7 では、`ConvertFrom-Json -AsHashtable` とパラメータ指定するとハッシュテーブルに変換してくれる。7を使える環境なら悩まなくていい。

[ConvertFrom-Json(公式ドキュメント)](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-json?view=powershell-7)
[[PowerShell 7] ConvertFrom-Json を使用して JSON データを読み込む](https://blog.powershell-from.jp/?p=2553#toc4)

# 追記：JavaScriptSerializer.Deserializeメソッドで変換

`JavaScriptSerializer.Deserialize`メソッドでJSONをハッシュテーブルに変換できる。PowerShell 7でなくても使える。(ver5で確認)

[PowerShell で JSON 変換をするときに発生するアレコレ](https://qiita.com/c-yan/items/7d0c916ed6f30b3775dd#a4-systemwebscriptserializationjavascriptserializer-%E3%82%92%E4%BD%BF%E3%81%86)
[JavaScriptSerializer クラス](https://docs.microsoft.com/ja-jp/dotnet/api/system.web.script.serialization.javascriptserializer?view=netframework-4.8)
[JavaScriptSerializer.Deserializeメソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.web.script.serialization.javascriptserializer.deserialize?view=netframework-4.8)