2013-05-31 11 views
13

Ho riscontrato un problema durante l'utilizzo di PowerShell v3 durante la conversione di stringhe JSON di dimensioni superiori a 2 MB. Il limite predefinito nel serializzatore JSON utilizzato da PowerShell è impostato su 2 MB che spiega l'errore.Lunghezza massima ConvertFrom-Json

Tuttavia, quando deserializzo l'oggetto utilizzando ConvertFrom-Json su un set più piccolo (ho vari oggetti dati con raccolte interne più piccole e più grandi ma sono gli stessi oggetti) restituisce oggetti molto belli con tutte le proprietà a cui posso accedere facilmente.

Per superare i limiti del serializzatore ho cercato di deserializzare dati manualmente:

$jsser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 
$jsser.MaxJsonLength = $jsser.MaxJsonLength * 10 
$jsser.RecursionLimit = 99  

$outObject = $jsser.DeserializeObject($json) 

L'oggetto appare diverso sembra che collezioni interne non erano deserializzate e quando provo ad eseguire proprietà ritornano risultati vuoti.

Le mie domande:

  1. presupposto è ConvertFrom-Json fa qualche magia supplementare o in qualche modo crea un modello per l'oggetto prima di serializzazione. Qualche idea su come replicarlo?

  2. L'oggetto che ottengo è sempre un PSCustomObject; se ottengo l'oggetto che voglio impostare da ConvertFrom-Json c'è comunque da usare come tipo di oggetto in JsonSerializer?

risposta

0

ho messo questo nel mio codice:

 JavaScriptSerializer oSerializer = new JavaScriptSerializer(); 
    oSerializer.MaxJsonLength *= 2; 
    ws_Out = (ClsWsOut)oSerializer.Deserialize(jsonOut, ws_Out.GetType()); 

Dove ws_Out.GetType() è una classe definisco di analizzare il JSON.

public class ClsLogin_In :ClsWsIn 
{ 
    public string login { get; set; } 
    public string passwd { get; set; } 
} 

public class ClsLogin_Out : ClsWsOut 
{ 
    public int error { get; set; } 
    public string error_desc { get; set; } 
    public int key { get; set; } 
} 

Redatta

in PowerShell V3 quando il JSON restituita da un servizio web è molto grande la PowerShell V3 sta inviando un'eccezione. Quindi uso la serializzazione XML, ecco la mia funzione, usa anche un assembly esterno, ma sono di base, XML è un po 'prolisso ma funziona.

Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization 
$utf8 = [System.Text.Encoding]::UTF8  

function Write-String 

{ 
    PARAM([Parameter()]$stream, 
     [Parameter(ValueFromPipeline=$true)]$string) 

    PROCESS 
    { 
    $bytes = $utf8.GetBytes($string) 
    $stream.Write($bytes, 0, $bytes.Length) 
    } 
} 

function Convert-JsonToXml 

{ 
    PARAM([Parameter(ValueFromPipeline=$true)][string[]]$json) 

    BEGIN 
    { 
    $mStream = New-Object System.IO.MemoryStream 
    } 

    PROCESS 
    { 
    $json | Write-String -stream $mStream 
    } 

    END 
    { 
    $mStream.Position = 0 
    try 
    { 
     $jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max) 
     $xml = New-Object Xml.XmlDocument 
     $xml.Load($jsonReader) 
     $xml 
    } 
    finally 
    { 
     $jsonReader.Close() 
     $mStream.Dispose() 
    } 
    } 
} 

function Convert-XmlToJson 
{ 
    PARAM([Parameter(ValueFromPipeline=$true)][Xml]$xml) 

    PROCESS 
    { 
    $mStream = New-Object System.IO.MemoryStream 
    $jsonWriter = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonWriter($mStream) 
    try 
    { 
     $xml.Save($jsonWriter) 
     $bytes = $mStream.ToArray() 
     [System.Text.Encoding]::UTF8.GetString($bytes,0,$bytes.Length) 
    } 
    finally 
    { 
     $jsonWriter.Close() 
     $mStream.Dispose() 
    } 
    } 
} 

Ecco un esempio.

$json = @' 
{ 
    "data": { 
    "langid": 7, 
    "results": [{ 
     "first_aired": "2010-11-15", 
     "name": "Accused", 
     "tvdbid": 72663 
     }, 
     { 
     "first_aired": "2010-01-17", 
     "name": "Enzai: Falsely Accused", 
     "tvdbid": 135881 
     }] 
    }, 
    "message": "", 
    "result": "success" 
} 
'@ 

$xmlOut = Convert-JsonToXml -json $json 
($xmlOut.root.data.results).ChildNodes[0].tvdbid.InnerText 
($xmlOut.root.data.results).ChildNodes[1].tvdbid.InnerText 
+0

Sì, ma in PowerShell sto cercando di evitare l'importazione.Net dll PowerShell ha i propri oggetti dinamici che sono popolati con proprietà al volo e funzionava alla grande con file sotto 2MB. Voglio solo forzarlo a funzionare allo stesso modo con file superiori a 2 MB. – Jammes

+0

Per essere completamente PowerShell è possibile utilizzare XML. Lo uso nei miei script PowerShell V2.0. Supporta grandi jsons, ma è un po 'prolisso da usare. – JPBlanc

+0

Se osservi la mia domanda iniziale, ho già trovato un modo per deserializzare JSON di grandi dimensioni. Quando si crea un'istanza del serializzatore, è possibile modificare la proprietà MaxJsonLength. Dovrai utilizzare Invoke-WebRequest piuttosto che Invoke-RestRequest e poi seguire RawContent e cercare il primo '{' e rimuovere tutto da infront e hai un json pulito. Su oggetti di grandi dimensioni questa serializzazione Xml potrebbe richiedere il suo pedaggio. – Jammes

16

Ho avuto lo stesso problema ed è stato in grado di risolvere in questo modo:

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")   
$jsonserial= New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer 
$jsonserial.MaxJsonLength = 67108864 
$Obj = $jsonserial.DeserializeObject($CourseTypesResponse) 

È possibile utilizzare $jsonserial.MaxJsonLength per manipolare la proprietà maxJsonLength

fonte: https://social.technet.microsoft.com/Forums/windowsserver/en-US/833c99c1-d8eb-400d-bf58-38f7265b4b0e/error-when-converting-from-json?forum=winserverpowershell&prof=required

3

ho avuto lo stesso propblem quando si utilizza Invoke-RestMethod e grandi raccolte JSON nel risultato. Ho finito per adattare i metodi da Parsing json with PowerShell and Json.NET per convertire le raccolte JSON in oggetti PowerShell.

# .NET JSON Serializer 
    $global:javaScriptSerializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer 
    $global:javaScriptSerializer.MaxJsonLength = [System.Int32]::MaxValue 
    $global:javaScriptSerializer.RecursionLimit = 99 

    # Functions necessary to parse JSON output from .NET serializer to PowerShell Objects 
    function ParseItem($jsonItem) { 
      if($jsonItem.PSObject.TypeNames -match "Array") { 
        return ParseJsonArray($jsonItem) 
      } 
      elseif($jsonItem.PSObject.TypeNames -match "Dictionary") { 
        return ParseJsonObject([HashTable]$jsonItem) 
      } 
      else { 
        return $jsonItem 
      } 
    } 

    function ParseJsonObject($jsonObj) { 
      $result = New-Object -TypeName PSCustomObject 
      foreach ($key in $jsonObj.Keys) { 
        $item = $jsonObj[$key] 
        if ($item) { 
          $parsedItem = ParseItem $item 
        } else { 
          $parsedItem = $null 
        } 
        $result | Add-Member -MemberType NoteProperty -Name $key -Value $parsedItem 
      } 
      return $result 
    } 

    function ParseJsonArray($jsonArray) { 
      $result = @() 
      $jsonArray | ForEach-Object { 
        $result += , (ParseItem $_) 
      } 
      return $result 
    } 

    function ParseJsonString($json) { 
      $config = $javaScriptSerializer.DeserializeObject($json) 
      return ParseJsonObject($config) 
    } 
+0

Grazie per la soluzione. Tuttavia, è meglio sostituire 'return ParseJsonObject ($ config)' con 'return ParseItem ($ config)' come 'DeserializeObject' potrebbe restituire array. –