# encoding: ascii
# api: powershell
# title: JSON
# description: Identical to Version 1.1, but code-signed.
# version: 1.2
# type: function
# author: Joel Bennett
# license: BDSL
# function: Write-String
# x-poshcode-id: 1907
# x-derived-from-id: 1908
# x-archived: 2016-08-30T08:28:13
# x-published: 2010-06-08T09:36:00
#
# In this JSON module, I have a full set of tools for exporting, importing, and converting Json objects (including arbitrary objects). See comments in script header for usage examples, but basically, you can do things like:
# ls | ConvertTo-Json -Depth 2
# ls | Select FullName, Length | ConvertTo-JSon -NoType
# You can do full round-trips, even with partial data, as long as you specify the Type when converting back from JSON:
# PS | Select PM, WS, CPU, ID, ProcessName | ConvertTo-json -NoType | ConvertFrom-json -Type System.Diagnostics.Process
# FIXED IN THIS VERSION:
# Minor change to ConvertFrom-Json to handle a single item being returned without an objects wrapper
#
#requires -version 2.0
# Version History:
# v 0.5 - First Public version
# v 1.0 - Made ConvertFrom-Json work with arbitrary JSON
# - switched to xsl style sheets for ConvertTo-JSON
# v 1.1 - Changed ConvertFrom-Json to handle single item results
# v 1.2 - CodeSigned to make a fellow geek happy
# There is no help (yet) because I'm still changing and renaming everything every time I mess with this code
# Full RoundTrip capability:
#
# > ls | ConvertTo-Json | ConvertFrom-Json
# > ps | ConvertTo-Json | Convert-JsonToXml | Convert-XmlToJson | convertFrom-Json
#
# You may frequently want to use the DEPTH or NoTypeInformation parameters:
#
# > ConvertTo-Json -Depth 2 -NoTypeInformation
#
# But then you have to specify the type when you reimport (and you can't do that for deep objects).
# This problem also occurs if you convert the result of a SELECT statement (ie: PSCustomObject).
# For Example:
#
# > PS | Select PM, WS, CPU, ID, ProcessName |
# >> ConvertTo-json -NoType |
# >> convertfrom-json -Type System.Diagnostics.Process
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
$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()
}
}
}
## Rather than rewriting ConvertTo-Xml ...
Function ConvertTo-Json {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)]$InputObject
,
[Parameter(Mandatory=$false)][Int]$Depth=1
,
[Switch]$NoTypeInformation
)
END {
## You must output ALL the input at once
## ConvertTo-Xml outputs differently if you just have one, so your results would be different
$input | ConvertTo-Xml -Depth:$Depth -NoTypeInformation:$NoTypeInformation -As Document | Convert-CliXmlToJson
}
}
Function Convert-CliXmlToJson {
PARAM(
[Parameter(ValueFromPipeline=$true)][Xml.XmlNode]$xml
)
BEGIN {
$xmlToJsonXsl = @'
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
CliXmlToJson.xsl
Copyright (c) 2006,2008 Doeke Zanstra
Copyright (c) 2009 Joel Bennett
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. Redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
-->
<xsl:output indent="no" omit-xml-declaration="yes" method="text" encoding="UTF-8" media-type="text/x-json"/>
<xsl:strip-space elements="*"/>
<!--contant-->
<xsl:variable name="d">0123456789</xsl:variable>
<!-- ignore document text -->
<xsl:template match="text()[preceding-sibling::node() or following-sibling::node()]"/>
<!-- string -->
<xsl:template match="text()">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="."/>
</xsl:call-template>
</xsl:template>
<!-- Main template for escaping strings; used by above template and for object-properties
Responsibilities: placed quotes around string, and chain up to next filter, escape-bs-string -->
<xsl:template name="escape-string">
<xsl:param name="s"/>
<xsl:text>"</xsl:text>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:template>
<!-- Escape the backslash (\) before everything else. -->
<xsl:template name="escape-bs-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'\')">
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="concat(substring-before($s,'\'),'\\')"/>
</xsl:call-template>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="substring-after($s,'\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Escape the double quote ("). -->
<xsl:template name="escape-quot-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'"')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'"'),'\"')"/>
</xsl:call-template>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="substring-after($s,'"')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Replace tab, line feed and/or carriage return by its matching escape code. Can't escape backslash
or double quote here, because they don't replace characters (� becomes \t), but they prefix
characters (\ becomes \\). Besides, backslash should be seperate anyway, because it should be
processed first. This function can't do that. -->
<xsl:template name="encode-string">
<xsl:param name="s"/>
<xsl:choose>
<!-- tab -->
<xsl:when test="contains($s,'	')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'	'),'\t',substring-after($s,'	'))"/>
</xsl:call-template>
</xsl:when>
<!-- line feed -->
<xsl:when test="contains($s,'
')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'
'),'\n',substring-after($s,'
'))"/>
</xsl:call-template>
</xsl:when>
<!-- carriage return -->
<xsl:when test="contains($s,'
')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'
'),'\r',substring-after($s,'
'))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$s"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- number (no support for javascript mantise) -->
<xsl:template match="text()[not(string(number())='NaN' or
(starts-with(.,'0' ) and . != '0'))]">
<xsl:value-of select="."/>
</xsl:template>
<!-- boolean, case-insensitive -->
<xsl:template match="text()[translate(.,'TRUE','true')='true']">true</xsl:template>
<xsl:template match="text()[translate(.,'FALSE','false')='false']">false</xsl:template>
<!-- root object(s) -->
<xsl:template match="*" name="base">
<xsl:if test="not(preceding-sibling::*)">
<xsl:choose>
<xsl:when test="count(../*)>1"><xsl:text>[</xsl:text></xsl:when>
<xsl:otherwise><xsl:text>{</xsl:text></xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<!-- check type of node -->
<xsl:choose>
<!-- null nodes -->
<xsl:when test="count(child::node())=0">null</xsl:when>
<!-- other nodes -->
<xsl:otherwise>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<!-- end of type check -->
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">
<xsl:choose>
<xsl:when test="count(../*)>1"><xsl:text>]</xsl:text></xsl:when>
<xsl:otherwise><xsl:text>}</xsl:text></xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- properties of objects -->
<xsl:template match="*[count(../*[name(../*)=name(.)])=count(../*) and count(../*)>1]">
<xsl:variable name="inArray" select="translate(local-name(),'OBJECT','object')='object' or ../@Type[starts-with(.,'System.Collections') or contains(.,'[]') or (contains(.,'[') and contains(.,']'))]"/>
<xsl:if test="not(preceding-sibling::*)">
<xsl:choose>
<xsl:when test="$inArray"><xsl:text>[</xsl:text></xsl:when>
<xsl:otherwise>
<xsl:text>{</xsl:text>
<xsl:if test="../@Type">
<xsl:text>"__type":</xsl:text>
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="../@Type"/>
</xsl:call-template>
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:choose>
<xsl:when test="not(child::node())">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="@Name"/>
</xsl:call-template>
<xsl:text>:null</xsl:text>
</xsl:when>
<xsl:when test="$inArray">
<xsl:apply-templates select="child::node()"/>
</xsl:when>
<!--
<xsl:when test="not(@Name) and not(@Type)">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="local-name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<xsl:apply-templates select="child::node()"/>
</xsl:when>
-->
<xsl:when test="not(@Name)">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="local-name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<xsl:apply-templates select="child::node()"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="@Name"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">
<xsl:choose>
<xsl:when test="$inArray"><xsl:text>]</xsl:text></xsl:when>
<xsl:otherwise><xsl:text>}</xsl:text></xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- convert root element to an anonymous container -->
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>
'@
}
PROCESS {
if(Get-Member -InputObject $xml -Name root) {
Write-Verbose "Ripping to Objects"
$xml = $xml.root.Objects
} else {
Write-Verbose "Was already Objects"
}
Convert-Xml -Xml $xml -Xsl $xmlToJsonXsl
}
}
Function ConvertFrom-Xml {
[CmdletBinding(DefaultParameterSetName="AutoType")]
PARAM(
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)]
[Xml.XmlNode]
$xml
,
[Parameter(Mandatory=$true,ParameterSetName="ManualType")]
[Type]$Type
,
[Switch]$ForceType
)
PROCESS{
if(Get-Member -InputObject $xml -Name root) {
return $xml.root.Objects |
} elseif(Get-Member -InputObject $xml -Name Objects) {
return $xml.Objects |
}
$propbag = @{}
foreach($name in Get-Member -InputObject $xml -MemberType Property | Where-Object{$_.Name -notmatch "^__|type"} | Select-Object -ExpandProperty name) {
Write-Verbose "$Name Type: $($xml."$Name".type)"
switch( $xml."$Name".type ) {
"object" {
$propbag."$Name" = ConvertFrom-Xml -Xml $xml."$Name"
break
}
"string" {
$propbag."$Name" = $xml."$Name".get_InnerText()
$MightBeADate = $xml."$Name".get_InnerText() -as [DateTime]
## Strings that are actually dates (*grumble* JSON is crap)
if($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
$propbag."$Name" = $MightBeADate
}
break
}
"number" {
$number = $xml."$Name".get_InnerText()
if($number -eq ($number -as [int])) {
$propbag."$Name" = $number -as [int]
} elseif($number -eq ($number -as [double])) {
$propbag."$Name" = $number -as [double]
} else {
$propbag."$Name" = $number -as [decimal]
}
break
}
"boolean" {
$propbag."$Name" = [bool]::parse($xml."$Name".get_InnerText())
}
"null" {
$propbag."$Name" = $null
}
default {
$propbag."$Name" = $xml."$Name"
break
}
}
}
if(!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
if($ForceType -and $Type) {
try {
$output = New-Object $Type -Property $propbag
} catch {
$output = New-Object PSObject -Property $propbag
$output.PsTypeNames.Insert(0, $xml.__type)
}
} else {
$output = New-Object PSObject -Property $propbag
if($Type) {
$output.PsTypeNames.Insert(0, $Type)
}
}
Write-Output $output
}
}
Function ConvertFrom-Json {
[CmdletBinding(DefaultParameterSetName="AutoType")]
PARAM(
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)][string]$InputObject
,
[Parameter(Mandatory=$true,ParameterSetName="ManualType")][Type]$Type
,
[Switch]$ForceType
)
PROCESS {
$null = $PSBoundParameters.Remove("InputObject")
$xml = (Convert-JsonToXml $InputObject).Root
if($xml.Objects) {
$xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
}elseif($xml.Item) {
$xml.Item | ConvertFrom-Xml @PSBoundParameters
}
}
}
#########
### The JSON library is dependent on Convert-Xml from my Xml script module
function Convert-Node {
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Xml.XmlReader]$XmlReader,
[Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$false)]
[System.Xml.Xsl.XslCompiledTransform]$StyleSheet
)
PROCESS {
$output = New-Object IO.StringWriter
$StyleSheet.Transform( $XmlReader, $null, $output )
Write-Output $output.ToString()
}
}
function Convert-Xml {
#.Synopsis
# The Convert-XML function lets you use Xslt to transform XML strings and documents.
#.Description
#.Parameter Content
# Specifies a string that contains the XML to search. You can also pipe strings to Select-XML.
#.Parameter Namespace
# Specifies a hash table of the namespaces used in the XML. Use the format @{<namespaceName> = <namespaceUri>}.
#.Parameter Path
# Specifies the path and file names of the XML files to search. Wildcards are permitted.
#.Parameter Xml
# Specifies one or more XML nodes to search.
#.Parameter Xsl
# Specifies an Xml StyleSheet to transform with...
[CmdletBinding(DefaultParameterSetName="Xml")]
PARAM(
[Parameter(Position=1,ParameterSetName="Path",Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("PSPath")]
[String[]]$Path
,
[Parameter(Position=1,ParameterSetName="Xml",Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("Node")]
[System.Xml.XmlNode[]]$Xml
,
[Parameter(ParameterSetName="Content",Mandatory=$true,ValueFromPipeline=$true)]
[ValidateNotNullOrEmpty()]
[String[]]$Content
,
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
[ValidateNotNullOrEmpty()]
[Alias("StyleSheet")]
[String[]]$Xslt
)
BEGIN {
$StyleSheet = New-Object System.Xml.Xsl.XslCompiledTransform
if(Test-Path @($Xslt)[0] -ErrorAction 0) {
Write-Verbose "Loading Stylesheet from $(Resolve-Path @($Xslt)[0])"
$StyleSheet.Load( (Resolve-Path @($Xslt)[0]) )
} else {
Write-Verbose "$Xslt"
$StyleSheet.Load(([System.Xml.XmlReader]::Create((New-Object System.IO.StringReader ($Xslt -join "`n")))))
}
[Text.StringBuilder]$XmlContent = [String]::Empty
}
PROCESS {
switch($PSCmdlet.ParameterSetName) {
"Content" {
$null = $XmlContent.AppendLine( $Content -Join "`n" )
}
"Path" {
foreach($file in Get-ChildItem $Path) {
Convert-Node -Xml ([System.Xml.XmlReader]::Create((Resolve-Path $file))) $StyleSheet
}
}
"Xml" {
foreach($node in $Xml) {
Convert-Node -Xml (New-Object Xml.XmlNodeReader $node) $StyleSheet
}
}
}
}
END {
if($PSCmdlet.ParameterSetName -eq "Content") {
[Xml]$Xml = $XmlContent.ToString()
Convert-Node -Xml $Xml $StyleSheet
}
}
}
New-Alias fromjson ConvertFrom-Json
New-Alias tojson ConvertTo-Json
#New-Alias ipjs Import-Json
#New-Alias epjs Export-Json
#Import-Json, Export-Json,
Export-ModuleMember -Function ConvertFrom-Json, ConvertTo-Json, Convert-JsonToXml, Convert-XmlToJson, Convert-CliXmlToJson -Alias *
# SIG # Begin signature block
# MIIIDQYJKoZIhvcNAQcCoIIH/jCCB/oCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUfmiIa5vfHY0pXkdWMbqjmY43
# ZR+gggUrMIIFJzCCBA+gAwIBAgIQKQm90jYWUDdv7EgFkuELajANBgkqhkiG9w0B
# AQUFADCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
# IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYD
# VQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VS
# Rmlyc3QtT2JqZWN0MB4XDTEwMDUxNDAwMDAwMFoXDTExMDUxNDIzNTk1OVowgZUx
# CzAJBgNVBAYTAlVTMQ4wDAYDVQQRDAUwNjg1MDEUMBIGA1UECAwLQ29ubmVjdGlj
# dXQxEDAOBgNVBAcMB05vcndhbGsxFjAUBgNVBAkMDTQ1IEdsb3ZlciBBdmUxGjAY
# BgNVBAoMEVhlcm94IENvcnBvcmF0aW9uMRowGAYDVQQDDBFYZXJveCBDb3Jwb3Jh
# dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMfUdxwiuWDb8zId
# KuMg/jw0HndEcIsP5Mebw56t3+Rb5g4QGMBoa8a/N8EKbj3BnBQDJiY5Z2DGjf1P
# n27g2shrDaNT1MygjYfLDntYzNKMJk4EjbBOlR5QBXPM0ODJDROg53yHcvVaXSMl
# 498SBhXVSzPmgprBJ8FDL00o1IIAAhYUN3vNCKPBXsPETsKtnezfzBg7lOjzmljC
# mEOoBGT1g2NrYTq3XqNo8UbbDR8KYq5G101Vl0jZEnLGdQFyh8EWpeEeksv7V+YD
# /i/iXMSG8HiHY7vl+x8mtBCf0MYxd8u1IWif0kGgkaJeTCVwh1isMrjiUnpWX2NX
# +3PeTmsCAwEAAaOCAW8wggFrMB8GA1UdIwQYMBaAFNrtZHQUnBQ8q92Zqb1bKE2L
# PMnYMB0GA1UdDgQWBBTK0OAaUIi5wvnE8JonXlTXKWENvTAOBgNVHQ8BAf8EBAMC
# B4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhC
# AQEEBAMCBBAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwIwKzApBggrBgEFBQcC
# ARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLm5ldC9DUFMwQgYDVR0fBDswOTA3oDWg
# M4YxaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0
# LmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNv
# bW9kb2NhLmNvbTAhBgNVHREEGjAYgRZKb2VsLkJlbm5ldHRAWGVyb3guY29tMA0G
# CSqGSIb3DQEBBQUAA4IBAQAEss8yuj+rZvx2UFAgkz/DueB8gwqUTzFbw2prxqee
# zdCEbnrsGQMNdPMJ6v9g36MRdvAOXqAYnf1RdjNp5L4NlUvEZkcvQUTF90Gh7OA4
# rC4+BjH8BA++qTfg8fgNx0T+MnQuWrMcoLR5ttJaWOGpcppcptdWwMNJ0X6R2WY7
# bBPwa/CdV0CIGRRjtASbGQEadlWoc1wOfR+d3rENDg5FPTAIdeRVIeA6a1ZYDCYb
# 32UxoNGArb70TCpV/mTWeJhZmrPFoJvT+Lx8ttp1bH2/nq6BDAIvu0VGgKGxN4bA
# T3WE6MuMS2fTc1F8PCGO3DAeA9Onks3Ufuy16RhHqeNcMYICTDCCAkgCAQEwgaow
# gZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl
# IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY
# aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0
# LU9iamVjdAIQKQm90jYWUDdv7EgFkuELajAJBgUrDgMCGgUAoHgwGAYKKwYBBAGC
# NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUoUwjRzCv
# 3+BoGsB+b0S8utgrxNIwDQYJKoZIhvcNAQEBBQAEggEAuWvNn+zWBq99vDMSLtfF
# +8UIONZYIASIwWQXepjKqLbwmZsQaBDGFEDgXJCnpjK+1xbORJPREAPCPvEBXA4L
# HplhYl9HUQ/2GezBXc+D3AVkG34GoBWEIZzkud2jH+DMFIWDIQP8RJJ3YkYoUaL5
# 5qAsgeASTHkWI+8fG7Nume2EeGZVNuKLQK7vacSS4A+WkNXojtgYsLvlL1tVlGEA
# 7xURgoYqhfgU8NApViTDYKBE2ohXXtojb6CrDHN1DUF1QWxlAFQdODjDR05pdqq9
# sITi8drKtHcGz37q7MVkGSX0VX5FSvDE/Mr8BcIAQONET+zDOHr7tGB55DxT8ZIv
# 2A==
# SIG # End signature block