Converter para csv de json em powershell

Sep 23 2020

este é meu csv:

"name,data
Play,http://{gho}.domain.com/
BDomain,domain.com
Charts,2
Compress,0
CompressJ,0" | ConvertFrom-Csv | ConvertTo-Json

Dá-me:

[
{
    "name":  "Play",
    "data":  "http://{gho}.domain.com/"
},
{
    "name":  "BDomain",
    "data":  "domain.com"
},
{
    "name":  "Charts",
    "data":  "2"
},
{
    "name":  "Compress",
    "data":  "0"
},
{
    "name":  "CompressJ",
    "data":  "0"
}
]

Agora, gostaria de obter esse csv de volta do meu json, mas as cadeias de | ConvertFrom-Json | O ConvertTo-CSV não está funcionando e estou curioso para saber por que não?

Respostas

1 mklement0 Sep 25 2020 at 05:11

Para complementar a resposta útil de Steven com um resumo sucinto e uma perspectiva de várias edições:

  • No PowerShell [Core] v7 + , nenhum esforço extra é necessário: anexar | ConvertFrom-Json | ConvertTo-Csvao seu código funciona como está.

  • No Windows PowerShell e PowerShell [Core] v6.x , você precisa forçar a enumeração da ConvertFrom-Jsonsaída de ConvertTo-Csvpara funcionar corretamente, porque, contra o comportamento normal do PowerShell, ConvertFrom-Jsongera uma matriz JSON como um todo, como um único objeto para o pipeline .

    • É o fato de que esse comportamento é incomum que levou à mudança na v7 + - consulte esta resposta para obter informações básicas.

    • A maneira mais simples de forçar a enumeração - ou seja, enviar elementos de uma coleção / array de saída como um todo, um por um, para o pipeline é usar (...)o operador de agrupamento .

# Works in all PowerShell versions, but in v7+ the (...) is no longer necessary.
(
"name,data
Play,http://{gho}.domain.com/
BDomain,domain.com
Charts,2
Compress,0
CompressJ,0" | ConvertFrom-Csv | ConvertTo-Json |
  ConvertFrom-Json
) | ConvertTo-Csv -NoTypeInformation

Isso produz:

"name","data"
"Play","http://{gho}.domain.com/"
"BDomain","domain.com"
"Charts","2"
"Compress","0"
"CompressJ","0"
2 Steven Sep 22 2020 at 23:59

A saída de ConvertFrom-Jsonuma única matriz sendo passada pelo pipeline como tal. Isso é uma consequência de como ConvertFrom-Jsonestá escrevendo sua saída. Provavelmente, ele está coletando tudo e gerando o array em um Write-Outputcomando (implícito ou não) em vez de fazer o streaming de cada objeto pelo pipeline à medida que são criados. É provavelmente uma consequência de como o cmdlet foi escrito e pode ter sido necessário.

Seu código atual provavelmente está gerando um csv como:

"Count","Length","LongLength","Rank","SyncRoot","IsReadOnly","IsFixedSize","IsSynchronized"
"5","5","5","1","System.Object[]","False","True","False"

Essas são as propriedades da matriz, não os objetos nela. Uma maneira de contornar isso é estacionar os dados em uma variável:

$Json = "name,data Play,http://{gho}.domain.com/ BDomain,domain.com Charts,2 Compress,0 CompressJ,0" | ConvertFrom-Csv | ConvertTo-Json # Now you can convert back to csv without issue: $Json = $Json | ConvertFrom-Json $Json | ConvertTo-Csv -NoTypeInformation

Nota: se você enviar $ Json diretamente como $Json | ConvertFrom-Json | ConvertTo-Csv...você pode ter o mesmo problema.

Iterar sobre os objetos Json também parece funcionar:

$Json = "name,data Play,http://{gho}.domain.com/ BDomain,domain.com Charts,2 Compress,0 CompressJ,0" | ConvertFrom-Csv | ConvertTo-Json $Json | 
ConvertFrom-Json |
ForEach-Object{ $_ } | 
ConvertTo-Csv -NoTypeInformation

Graças a @notjustme, você pode encurtar isso usando (...)como:

($json | ConvertFrom-Json) | ConvertTo-Csv

Nota: corrigido de acordo com a resposta de mklement0 .

Todas essas opções são essencialmente posicionar a matriz na frente do tubo para contornar o comportamento interno de ConvertFrom-Json

Mais uma vez, não tenho 100% de certeza por que ConvertFrom-Json se comporta dessa maneira. Atualizarei com informações adicionais quando encontrar uma explicação mais formal.