Summary: June Blender provides a primer about JSON.
Honorary Scripting Guy, June Blender, here. Today I'm going to introduce you to JSON.
June is a writer for the Azure Active Directory SDK. She is also a frequent contributor to the Hey, Scripting Guy! Blog and for PowerShell.org. She lives in magnificent Escalante, Utah, where she works remotely when she's not out hiking, kayaking, or convincing lost tourists to try Windows PowerShell. She believes that outstanding documentation is a collaborative effort, and she welcomes your comments and contributions. Follow her on Twitter at @juneb_get_help. To read more by June, see these Hey, Scripting Guy! Blog posts.
I've been having a lot of fun learning web programming and working in Microsoft Azure and Azure PowerShell. I've noticed that I'm encountering a lot more JSON and a lot less XML over time. So, I thought I'd give our beginners a little primer on JSON.
JavaScript Object Notation (JSON) is a "lightweight data-interchange format." It is a way for programs to talk to each other, which is easy for humans to read and write, and easy for machines to parse and generate. It's also really compressible. Unlike XML, which is wordy, you can squish JSON into a very few bytes so it's small enough to include in fields with character limits, like the headers in the HTTP requests that web programs use to communicate.
A JSON document is a string that looks like a hash table with name=value (or name:value) pairs, such as {"Color"="Purple"} and {"State":"Utah"}. It allows nesting, such as the "address" element this Wikipedia example:
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"height_cm": 167.64,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
}
}
JSON documents that are used for interprogram communication are based on a schema. The schemas are also written in JSON and are easy to interpret. You can use a JSON schema to determine how to write a JSON document, and then after writing, use it to validate a JSON document.
Fortunately, JSON is very easy to manage in Windows PowerShell. The ConvertFrom-Json cmdlet converts the JSON object into a custom object (PSCustomObject). JSON is case-sensitive, but the custom objects are case-insensitive.
To get a JSON string from a JSON file, use the Get-Content cmdlet with its Raw parameter.
PS C:\> Get-Content -Raw -Path .\myJson.json
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"height_cm": 167.64,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
}
}
To convert it to a custom object, pipe the JSON string to the ConvertFrom-Json cmdlet:
PS C:\> $j = Get-Content -Raw -Path .\myJson.json | ConvertFrom-Json
PS C:\> $j
firstName : John
lastName : Smith
isAlive : True
age : 25
height_cm : 167.64
address : @{streetAddress=21 2nd Street; city=New York; state=NY; postalCode=10021-3100}
PS C:\> $j.address
streetAddress city state postalCode
------------- ---- ----- ----------
21 2nd Street New York NY 10021-3100
The Raw parameter tells Get-Content to ignore line breaks and return a single string. You can tell how many strings you have by counting the number of objects that are returned. Without Raw, you get 13 separate strings. With Raw, you get a single string.
PS C:\> (Get-Content -Path .\myJson.json).count
13
PS C:\> (Get-Content -Path .\myJson.json -Raw).count
1
If you forget the Raw parameter and pipe multiple strings to ConvertFrom-Json, you get this distinctive error message, which is Pig Latin for "Did you forget the Raw parameter?"
ConvertFrom-Json : Invalid object passed in, ':' or '}' expected. (1): {
At line:1 char:20
+ cat .\myJson.json | ConvertFrom-Json
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [ConvertFrom-Json], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand
The ConvertFrom-Json cmdlet converts each JSON string into a custom object. It converts each name-value pair into a note property and its value.
PS C:\> $j = Get-Content -Raw -Path .\myJson.json | ConvertFrom-Json
PS C:\> $j | 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()
address NoteProperty System.Management.Automation.PSCustomObject
age NoteProperty System.Int32 age=25
firstName NoteProperty System.String firstName=John
height_cm NoteProperty System.Decimal height_cm=167.64
isAlive NoteProperty System.Boolean isAlive=True
lastName NoteProperty System.String lastName=Smith
For example, it converts this:
"firstName": "John"
…into a firstName note property with a value of John:
PS C:\> $j.firstName
John
When it encounters a nested JSON object, like the one in the value of address, it converts the value into a nested custom object with note properties representing each name-value pair.
For example, it converts this:
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
}
To:
PS C:\> $j.address
streetAddress city state postalCode
------------- ---- ----- ----------
21 2nd Street New York NY 10021-3100
…which is a custom object with its own note properties:
PS C:\> $j.address | 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()
city NoteProperty System.String city=New York
postalCode NoteProperty System.String postalCode=10021-3100
state NoteProperty System.String state=NY
streetAddress NoteProperty System.String streetAddress=21 2nd Street
You can edit the custom object and then use the ConvertTo-Json cmdlet and Set-Content cmdlets to replace the value in the .json file. Let's move John Smith to a more scenic location:
$j.address.city = "Escalante"
$j.address.state = "UT"
$j.address.postalCode = "84726"
PS C:\> $j
firstName : John
lastName : Smith
isAlive : True
age : 25
height_cm : 167.64
address : @{streetAddress=21 2nd Street; city=Escalante; state=UT; postalCode=84726}
Now, convert the custom object back to JSON...
PS C:\> $j | ConvertTo-Json
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"height_cm": 167.64,
"address": {
"streetAddress": "21 2nd Street",
"city": "Escalante",
"state": "UT",
"postalCode": "84726"
}
}
…and replace the content in the myJson.json file:
PS C:\> $j | ConvertTo-Json | Set-Content -Path .\myJson.json
Now you're ready to work with more complex JSON strings, such as the new Azure Gallery templates. More on that subject in another post.
I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum.
June Blender, senior programming writer and Microsoft Scripting Guy