# encoding: ascii
# api: powershell
# title: JSON
# description: Json.psm1 is a first draft of a JSON module. I has a full set of tools for exporting, importing, and converting Json objects. For instance, this actually works (it round trips a bunch of FileInfo objects through JSON, XML, JSON, and back to objects).
# version: 0.1
# type: function
# author: Joel Bennett
# license: CC0
# function: Convert-JsonToXml
# x-poshcode-id: 1513
# x-archived: 2012-07-10T01:35:14
# x-published: 2012-12-07T09:55:00
#
# ls | Where { !$_.PSIsContainer } | ConvertTo-Json | Convert-JsonToXml | Convert-XmlToJson | ConvertFrom-Json IO.FileInfo[] 
# However, this module is still very incomplete
#
#requires -version 2.0
# No help (yet) because I'm still changing and renaming everything every time I mess with this code
Add-Type -Assembly System.ServiceModel.Web, System.Runtime.Serialization
$utf8 = [System.Text.Encoding]::UTF8


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
   $jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
   try
   {
      $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()
   }
}
}



function ConvertFrom-Json {
PARAM( [Parameter(Mandatory=$true)][Type[]]$type, [Parameter(ValueFromPipeline=$true,Mandatory=$true)][String]$json )
PROCESS{ 
   $ms = New-object IO.MemoryStream (,$utf8.GetBytes($json))
   Import-Json $type $ms 
   $ms.dispose()
}
}

function Import-Json {
[CmdletBinding(DefaultParameterSetName="File")]
PARAM( 
[Parameter(Mandatory=$true,Position=1)][Type[]]$type
, 
[Parameter(Mandatory=$true,Position=2,ParameterSetName="Stream")][IO.Stream]$Stream 
, 
[Parameter(Mandatory=$true,Position=2,ParameterSetName="File")][String]$Path
)
BEGIN {
   if($PSCmdlet.ParameterSetName -eq "File") {
      $Stream = [IO.File]::Open($Path, "Read")
   }
}
PROCESS{
   if($type.Count -gt 1) {
      $t,$types = @($type)
      $js = New-Object System.Runtime.Serialization.Json.DataContractJsonSerializer $t, (,@($types))
   } else {
      $js = New-Object System.Runtime.Serialization.Json.DataContractJsonSerializer @($type)[0] 
   }
   Write-Output $js.ReadObject($Stream)
}
END {
   if($PSCmdlet.ParameterSetName -eq "File") {
      $Stream.Dispose()
   }
}
}

function Export-Json {
[CmdletBinding(DefaultParameterSetName="File")]
PARAM( 
[Parameter(Mandatory=$true,Position=1)][Array]$InputObject
, 
[Parameter(Mandatory=$true,Position=2,ParameterSetName="Stream")][IO.Stream]$Stream 
, 
[Parameter(Mandatory=$true,Position=2,ParameterSetName="File")][String]$Path
)
BEGIN {
   if($PSCmdlet.ParameterSetName -eq "File") {
      $Stream = [IO.File]::Open($Path, "Write")
   }
}
PROCESS {
   [type]$Type = @($InputObject)[0].GetType()

   if($Type -isnot [Array]) { #$InputObject.Count -gt 1 -and 
      [type]$Type = "$($Type)[]"
   }
   
   [Type[]]$types = ($InputObject | select -expand PsTypeNames) | % { $_ -split "`n" -replace "^Selected\." } | Select -unique
   
   #Write-Verbose $($Types | select -expand FullName | out-string)
   #Write-Verbose "Stream: $($Stream.GetType())"
   Write-Verbose "Output: $Type"
   Write-Verbose "Input: $($InputObject.GetType())"
   
   $js = New-Object System.Runtime.Serialization.Json.DataContractJsonSerializer $Type #, $Types #, ([int]::MaxValue), $false, $null, $false
   $js.WriteObject( $stream, $InputObject )
}
END {
   if($PSCmdlet.ParameterSetName -eq "File") {
      $Stream.Dispose()
   }
}
}


function ConvertTo-Json {
PARAM( [Parameter(ValueFromPipeline=$true,Mandatory=$true)]$object )
BEGIN {    
   [type]$lastType = $null
   function Out-JsonString {
      Param($items)
      $ms = New-Object IO.MemoryStream
      Export-Json $items.ToArray() $ms
      $utf8.GetString( $ms.ToArray(), 0, $ms.Length )
      $ms.Dispose()
   }
}
PROCESS {
   $thisType = $object.GetType()
   if(!$lastType -or $lastType -ne $thisType) { 
      if($lastType) { Out-JsonString $items }
      # make a new collection
      $items = new-object "System.Collections.Generic.List[$thisType]"
   }
   $items.Add($object)
   $lastType = $thisType
}
END {
   Out-JsonString $items
}
}

function Write-String {
param([Parameter()]$stream,[Parameter(ValueFromPipeline=$true)]$string)
process {
  $bytes = $utf8.GetBytes($string)
  $stream.Write( $bytes, 0, $bytes.Length )
}  
}
New-Alias fromjson ConvertFrom-Json
New-Alias tojson ConvertTo-Json

New-Alias cvfjs ConvertFrom-Json
New-Alias cvtjs ConvertTo-Json
New-Alias ipjs Import-Json
New-Alias epjs Export-Json


Export-ModuleMember -Function ConvertFrom-Json, Import-Json, Export-Json, ConvertTo-Json, Convert-JsonToXml, Convert-XmlToJson -Alias *
