PoshCode Logo PowerShell Code Repository

ProcuriosJSON.psm1 (modification of post by view diff)
embed code: <script type="text/javascript" src="http://PoshCode.org/embed/1529"></script>download | new post

Convert from JSON string to a hashtable or PSObject. e.g.:
$json | ConvertFrom-JSON -AsPSObject

The CSharp class embedded after the PowerShell wrapper function has an Encode method as well, but I haven’t played with it yet. All credit for the CSharp code goes to http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html.

  1. function ConvertFrom-Json {
  2.         param (
  3.                 [Parameter(     Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
  4.                 [Alias("Json")]
  5.                 [string[]]
  6.                 $InputObject,
  7.                 [switch]$AsPSObject = $false
  8.         )
  9.         Process {
  10.                 $output = [procurios.Public.JSON]::JsonDecode( $InputObject )
  11.                 if ( $AsPSObject ) {
  12.                         $output | ForEach-Object { New-Object PSObject -Property $_ }
  13.                 }
  14.                 else { Write-Output $output }
  15.         }
  16. }
  17.  
  18. #REGION IMPORT CSHARP TYPES
  19. # Source from: http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
  20. Add-Type @"
  21. using System;
  22. //using System.Data;
  23. using System.Collections;
  24. using System.Globalization;
  25. using System.Text;
  26.  
  27. namespace Procurios.Public
  28. {
  29.         /// <summary>
  30.         /// This class encodes and decodes JSON strings.
  31.         /// Spec. details, see http://www.json.org/
  32.         ///
  33.         /// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
  34.         /// All numbers are parsed to doubles.
  35.         /// </summary>
  36.         public class JSON
  37.         {
  38.                 public const int TOKEN_NONE = 0;
  39.                 public const int TOKEN_CURLY_OPEN = 1;
  40.                 public const int TOKEN_CURLY_CLOSE = 2;
  41.                 public const int TOKEN_SQUARED_OPEN = 3;
  42.                 public const int TOKEN_SQUARED_CLOSE = 4;
  43.                 public const int TOKEN_COLON = 5;
  44.                 public const int TOKEN_COMMA = 6;
  45.                 public const int TOKEN_STRING = 7;
  46.                 public const int TOKEN_NUMBER = 8;
  47.                 public const int TOKEN_TRUE = 9;
  48.                 public const int TOKEN_FALSE = 10;
  49.                 public const int TOKEN_NULL = 11;
  50.  
  51.                 private const int BUILDER_CAPACITY = 2000;
  52.  
  53.                 protected static JSON instance = new JSON();
  54.  
  55.                 /// <summary>
  56.                 /// On decoding, this value holds the position at which the parse failed (-1 = no error).
  57.                 /// </summary>
  58.                 protected int lastErrorIndex = -1;
  59.                 protected string lastDecode = "";
  60.  
  61.                 /// <summary>
  62.                 /// Parses the string json into a value
  63.                 /// </summary>
  64.                 /// <param name="json">A JSON string.</param>
  65.                 /// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
  66.                 public static object JsonDecode(string json)
  67.                 {
  68.                         // save the string for debug information
  69.                         JSON.instance.lastDecode = json;
  70.  
  71.                         if (json != null) {
  72.                char[] charArray = json.ToCharArray();
  73.                int index = 0;
  74.                                 bool success = true;
  75.                                 object value = JSON.instance.ParseValue(charArray, ref index, ref success);
  76.                                 if (success) {
  77.                                         JSON.instance.lastErrorIndex = -1;
  78.                                 } else {
  79.                                         JSON.instance.lastErrorIndex = index;
  80.                                 }
  81.                                 return value;
  82.            } else {
  83.                return null;
  84.            }
  85.                 }
  86.  
  87.                 /// <summary>
  88.                 /// Converts a Hashtable / ArrayList object into a JSON string
  89.                 /// </summary>
  90.                 /// <param name="json">A Hashtable / ArrayList</param>
  91.                 /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
  92.                 public static string JsonEncode(object json)
  93.                 {
  94.                         StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
  95.                         bool success = JSON.instance.SerializeValue(json, builder);
  96.                         return (success ? builder.ToString() : null);
  97.                 }
  98.  
  99.                 /// <summary>
  100.                 /// On decoding, this function returns the position at which the parse failed (-1 = no error).
  101.                 /// </summary>
  102.                 /// <returns></returns>
  103.                 public static bool LastDecodeSuccessful()
  104.                 {
  105.                         return (JSON.instance.lastErrorIndex == -1);
  106.                 }
  107.  
  108.                 /// <summary>
  109.                 /// On decoding, this function returns the position at which the parse failed (-1 = no error).
  110.                 /// </summary>
  111.                 /// <returns></returns>
  112.                 public static int GetLastErrorIndex()
  113.                 {
  114.                         return JSON.instance.lastErrorIndex;
  115.                 }
  116.  
  117.                 /// <summary>
  118.                 /// If a decoding error occurred, this function returns a piece of the JSON string
  119.                 /// at which the error took place. To ease debugging.
  120.                 /// </summary>
  121.                 /// <returns></returns>
  122.                 public static string GetLastErrorSnippet()
  123.                 {
  124.                         if (JSON.instance.lastErrorIndex == -1) {
  125.                                 return "";
  126.                         } else {
  127.                                 int startIndex = JSON.instance.lastErrorIndex - 5;
  128.                                 int endIndex = JSON.instance.lastErrorIndex + 15;
  129.                                 if (startIndex < 0) {
  130.                                         startIndex = 0;
  131.                                 }
  132.                                 if (endIndex >= JSON.instance.lastDecode.Length) {
  133.                                         endIndex = JSON.instance.lastDecode.Length - 1;
  134.                                 }
  135.  
  136.                                 return JSON.instance.lastDecode.Substring(startIndex, endIndex - startIndex + 1);
  137.                         }
  138.                 }
  139.  
  140.                 protected Hashtable ParseObject(char[] json, ref int index)
  141.                 {
  142.                         Hashtable table = new Hashtable();
  143.                         int token;
  144.  
  145.                         // {
  146.                         NextToken(json, ref index);
  147.  
  148.                         bool done = false;
  149.                         while (!done) {
  150.                                 token = LookAhead(json, index);
  151.                                 if (token == JSON.TOKEN_NONE) {
  152.                                         return null;
  153.                                 } else if (token == JSON.TOKEN_COMMA) {
  154.                                         NextToken(json, ref index);
  155.                                 } else if (token == JSON.TOKEN_CURLY_CLOSE) {
  156.                                         NextToken(json, ref index);
  157.                                         return table;
  158.                                 } else {
  159.  
  160.                                         // name
  161.                                         string name = ParseString(json, ref index);
  162.                                         if (name == null) {
  163.                                                 return null;
  164.                                         }
  165.  
  166.                                         // :
  167.                                         token = NextToken(json, ref index);
  168.                                         if (token != JSON.TOKEN_COLON) {
  169.                                                 return null;
  170.                                         }
  171.  
  172.                                         // value
  173.                                         bool success = true;
  174.                                         object value = ParseValue(json, ref index, ref success);
  175.                                         if (!success) {
  176.                                                 return null;
  177.                                         }
  178.  
  179.                                         table[name] = value;
  180.                                 }
  181.                         }
  182.  
  183.                         return table;
  184.                 }
  185.  
  186.                 protected ArrayList ParseArray(char[] json, ref int index)
  187.                 {
  188.                         ArrayList array = new ArrayList();
  189.  
  190.                         // [
  191.                         NextToken(json, ref index);
  192.  
  193.                         bool done = false;
  194.                         while (!done) {
  195.                                 int token = LookAhead(json, index);
  196.                                 if (token == JSON.TOKEN_NONE) {
  197.                                         return null;
  198.                                 } else if (token == JSON.TOKEN_COMMA) {
  199.                                         NextToken(json, ref index);
  200.                                 } else if (token == JSON.TOKEN_SQUARED_CLOSE) {
  201.                                         NextToken(json, ref index);
  202.                                         break;
  203.                                 } else {
  204.                                         bool success = true;
  205.                                         object value = ParseValue(json, ref index, ref success);
  206.                                         if (!success) {
  207.                                                 return null;
  208.                                         }
  209.  
  210.                                         array.Add(value);
  211.                                 }
  212.                         }
  213.  
  214.                         return array;
  215.                 }
  216.  
  217.                 protected object ParseValue(char[] json, ref int index, ref bool success)
  218.                 {
  219.                         switch (LookAhead(json, index)) {
  220.                                 case JSON.TOKEN_STRING:
  221.                                         return ParseString(json, ref index);
  222.                                 case JSON.TOKEN_NUMBER:
  223.                                         return ParseNumber(json, ref index);
  224.                                 case JSON.TOKEN_CURLY_OPEN:
  225.                                         return ParseObject(json, ref index);
  226.                                 case JSON.TOKEN_SQUARED_OPEN:
  227.                                         return ParseArray(json, ref index);
  228.                                 case JSON.TOKEN_TRUE:
  229.                                         NextToken(json, ref index);
  230.                                         return Boolean.Parse("TRUE");
  231.                                 case JSON.TOKEN_FALSE:
  232.                                         NextToken(json, ref index);
  233.                                         return Boolean.Parse("FALSE");
  234.                                 case JSON.TOKEN_NULL:
  235.                                         NextToken(json, ref index);
  236.                                         return null;
  237.                                 case JSON.TOKEN_NONE:
  238.                                         break;
  239.                         }
  240.  
  241.                         success = false;
  242.                         return null;
  243.                 }
  244.  
  245.                 protected string ParseString(char[] json, ref int index)
  246.                 {
  247.                         string s = "";
  248.                         char c;
  249.  
  250.                         EatWhitespace(json, ref index);
  251.                        
  252.                         // "
  253.                         c = json[index++];
  254.  
  255.                         bool complete = false;
  256.                         while (!complete) {
  257.  
  258.                                 if (index == json.Length) {
  259.                                         break;
  260.                                 }
  261.  
  262.                                 c = json[index++];
  263.                                 if (c == '"') {
  264.                                         complete = true;
  265.                                         break;
  266.                                 } else if (c == '\\') {
  267.  
  268.                                         if (index == json.Length) {
  269.                                                 break;
  270.                                         }
  271.                                         c = json[index++];
  272.                                         if (c == '"') {
  273.                                                 s += '"';
  274.                                         } else if (c == '\\') {
  275.                                                 s += '\\';
  276.                                         } else if (c == '/') {
  277.                                                 s += '/';
  278.                                         } else if (c == 'b') {
  279.                                                 s += '\b';
  280.                                         } else if (c == 'f') {
  281.                                                 s += '\f';
  282.                                         } else if (c == 'n') {
  283.                                                 s += '\n';
  284.                                         } else if (c == 'r') {
  285.                                                 s += '\r';
  286.                                         } else if (c == 't') {
  287.                                                 s += '\t';
  288.                                         } else if (c == 'u') {
  289.                                                 int remainingLength = json.Length - index;
  290.                                                 if (remainingLength >= 4) {
  291.                                                         // fetch the next 4 chars
  292.                                                         char[] unicodeCharArray = new char[4];
  293.                                                         Array.Copy(json, index, unicodeCharArray, 0, 4);
  294.                                                         // parse the 32 bit hex into an integer codepoint
  295.                                                         uint codePoint = UInt32.Parse(new string(unicodeCharArray), NumberStyles.HexNumber);
  296.                                                         // convert the integer codepoint to a unicode char and add to string
  297.                                                         s += Char.ConvertFromUtf32((int)codePoint);
  298.                                                         // skip 4 chars
  299.                                                         index += 4;
  300.                                                 } else {
  301.                                                         break;
  302.                                                 }
  303.                                         }
  304.  
  305.                                 } else {
  306.                                         s += c;
  307.                                 }
  308.  
  309.                         }
  310.  
  311.                         if (!complete) {
  312.                                 return null;
  313.                         }
  314.  
  315.                         return s;
  316.                 }
  317.  
  318.                 protected double ParseNumber(char[] json, ref int index)
  319.                 {
  320.                         EatWhitespace(json, ref index);
  321.  
  322.                         int lastIndex = GetLastIndexOfNumber(json, index);
  323.                         int charLength = (lastIndex - index) + 1;
  324.                         char[] numberCharArray = new char[charLength];
  325.  
  326.                         Array.Copy(json, index, numberCharArray, 0, charLength);
  327.                         index = lastIndex + 1;
  328.                         return Double.Parse(new string(numberCharArray), CultureInfo.InvariantCulture);
  329.                 }
  330.  
  331.                 protected int GetLastIndexOfNumber(char[] json, int index)
  332.                 {
  333.                         int lastIndex;
  334.                         for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
  335.                                 if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) {
  336.                                         break;
  337.                                 }
  338.                         }
  339.                         return lastIndex - 1;
  340.                 }
  341.  
  342.                 protected void EatWhitespace(char[] json, ref int index)
  343.                 {
  344.                         for (; index < json.Length; index++) {
  345.                                 if (" \t\n\r".IndexOf(json[index]) == -1) {
  346.                                         break;
  347.                                 }
  348.                         }
  349.                 }
  350.  
  351.                 protected int LookAhead(char[] json, int index)
  352.                 {
  353.                         int saveIndex = index;
  354.                         return NextToken(json, ref saveIndex);
  355.                 }
  356.  
  357.                 protected int NextToken(char[] json, ref int index)
  358.                 {
  359.                         EatWhitespace(json, ref index);
  360.  
  361.                         if (index == json.Length) {
  362.                                 return JSON.TOKEN_NONE;
  363.                         }
  364.                        
  365.                         char c = json[index];
  366.                         index++;
  367.                         switch (c) {
  368.                                 case '{':
  369.                                         return JSON.TOKEN_CURLY_OPEN;
  370.                                 case '}':
  371.                                         return JSON.TOKEN_CURLY_CLOSE;
  372.                                 case '[':
  373.                                         return JSON.TOKEN_SQUARED_OPEN;
  374.                                 case ']':
  375.                                         return JSON.TOKEN_SQUARED_CLOSE;
  376.                                 case ',':
  377.                                         return JSON.TOKEN_COMMA;
  378.                                 case '"':
  379.                                         return JSON.TOKEN_STRING;
  380.                                 case '0': case '1': case '2': case '3': case '4':
  381.                                 case '5': case '6': case '7': case '8': case '9':
  382.                                 case '-':
  383.                                         return JSON.TOKEN_NUMBER;
  384.                                 case ':':
  385.                                         return JSON.TOKEN_COLON;
  386.                         }
  387.                         index--;
  388.  
  389.                         int remainingLength = json.Length - index;
  390.  
  391.                         // false
  392.                         if (remainingLength >= 5) {
  393.                                 if (json[index] == 'f' &&
  394.                                         json[index + 1] == 'a' &&
  395.                                         json[index + 2] == 'l' &&
  396.                                         json[index + 3] == 's' &&
  397.                                         json[index + 4] == 'e') {
  398.                                         index += 5;
  399.                                         return JSON.TOKEN_FALSE;
  400.                                 }
  401.                         }
  402.  
  403.                         // true
  404.                         if (remainingLength >= 4) {
  405.                                 if (json[index] == 't' &&
  406.                                         json[index + 1] == 'r' &&
  407.                                         json[index + 2] == 'u' &&
  408.                                         json[index + 3] == 'e') {
  409.                                         index += 4;
  410.                                         return JSON.TOKEN_TRUE;
  411.                                 }
  412.                         }
  413.  
  414.                         // null
  415.                         if (remainingLength >= 4) {
  416.                                 if (json[index] == 'n' &&
  417.                                         json[index + 1] == 'u' &&
  418.                                         json[index + 2] == 'l' &&
  419.                                         json[index + 3] == 'l') {
  420.                                         index += 4;
  421.                                         return JSON.TOKEN_NULL;
  422.                                 }
  423.                         }
  424.  
  425.                         return JSON.TOKEN_NONE;
  426.                 }
  427.  
  428.                 protected bool SerializeObjectOrArray(object objectOrArray, StringBuilder builder)
  429.                 {
  430.                         if (objectOrArray is Hashtable) {
  431.                                 return SerializeObject((Hashtable)objectOrArray, builder);
  432.                         } else if (objectOrArray is ArrayList) {
  433.                                 return SerializeArray((ArrayList)objectOrArray, builder);
  434.                         } else {
  435.                                 return false;
  436.                         }
  437.                 }
  438.  
  439.                 protected bool SerializeObject(Hashtable anObject, StringBuilder builder)
  440.                 {
  441.                         builder.Append("{");
  442.  
  443.                         IDictionaryEnumerator e = anObject.GetEnumerator();
  444.                         bool first = true;
  445.                         while (e.MoveNext()) {
  446.                                 string key = e.Key.ToString();
  447.                                 object value = e.Value;
  448.  
  449.                                 if (!first) {
  450.                                         builder.Append(", ");
  451.                                 }
  452.  
  453.                                 SerializeString(key, builder);
  454.                                 builder.Append(":");
  455.                                 if (!SerializeValue(value, builder)) {
  456.                                         return false;
  457.                                 }
  458.  
  459.                                 first = false;
  460.                         }
  461.  
  462.                         builder.Append("}");
  463.                         return true;
  464.                 }
  465.  
  466.                 protected bool SerializeArray(ArrayList anArray, StringBuilder builder)
  467.                 {
  468.                         builder.Append("[");
  469.  
  470.                         bool first = true;
  471.                         for (int i = 0; i < anArray.Count; i++) {
  472.                                 object value = anArray[i];
  473.  
  474.                                 if (!first) {
  475.                                         builder.Append(", ");
  476.                                 }
  477.  
  478.                                 if (!SerializeValue(value, builder)) {
  479.                                         return false;
  480.                                 }
  481.  
  482.                                 first = false;
  483.                         }
  484.  
  485.                         builder.Append("]");
  486.                         return true;
  487.                 }
  488.  
  489.                 protected bool SerializeValue(object value, StringBuilder builder)
  490.                 {
  491.                         if (value is string) {
  492.                                 SerializeString((string)value, builder);
  493.                         } else if (value is Hashtable) {
  494.                                 SerializeObject((Hashtable)value, builder);
  495.                         } else if (value is ArrayList) {
  496.                                 SerializeArray((ArrayList)value, builder);
  497.                         } else if (IsNumeric(value)) {
  498.                                 SerializeNumber(Convert.ToDouble(value), builder);
  499.                         } else if ((value is Boolean) && ((Boolean)value == true)) {
  500.                                 builder.Append("true");
  501.                         } else if ((value is Boolean) && ((Boolean)value == false)) {
  502.                                 builder.Append("false");
  503.                         } else if (value == null) {
  504.                                 builder.Append("null");
  505.                         } else {
  506.                                 return false;
  507.                         }
  508.                         return true;
  509.                 }
  510.  
  511.                 protected void SerializeString(string aString, StringBuilder builder)
  512.                 {
  513.                         builder.Append("\"");
  514.  
  515.                         char[] charArray = aString.ToCharArray();
  516.                         for (int i = 0; i < charArray.Length; i++) {
  517.                                 char c = charArray[i];
  518.                                 if (c == '"') {
  519.                                         builder.Append("\\\"");
  520.                                 } else if (c == '\\') {
  521.                                         builder.Append("\\\\");
  522.                                 } else if (c == '\b') {
  523.                                         builder.Append("\\b");
  524.                                 } else if (c == '\f') {
  525.                                         builder.Append("\\f");
  526.                                 } else if (c == '\n') {
  527.                                         builder.Append("\\n");
  528.                                 } else if (c == '\r') {
  529.                                         builder.Append("\\r");
  530.                                 } else if (c == '\t') {
  531.                                         builder.Append("\\t");
  532.                                 } else {
  533.                                         int codepoint = Convert.ToInt32(c);
  534.                                         if ((codepoint >= 32) && (codepoint <= 126)) {
  535.                                                 builder.Append(c);
  536.                                         } else {
  537.                                                 builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
  538.                                         }
  539.                                 }
  540.                         }
  541.  
  542.                         builder.Append("\"");
  543.                 }
  544.  
  545.                 protected void SerializeNumber(double number, StringBuilder builder)
  546.                 {
  547.                         builder.Append(Convert.ToString(number, CultureInfo.InvariantCulture));
  548.                 }
  549.  
  550.                 /// <summary>
  551.                 /// Determines if a given object is numeric in any way
  552.                 /// (can be integer, double, etc). C# has no pretty way to do this.
  553.                 /// </summary>
  554.                 protected bool IsNumeric(object o)
  555.                 {
  556.                         try {
  557.                                 Double.Parse(o.ToString());
  558.                         } catch (Exception) {
  559.                                 return false;
  560.                         }
  561.                         return true;
  562.                 }
  563.         }
  564. }
  565. "@
  566.  
  567. #ENDREGION
  568.  
  569. Export-ModuleMember ConvertFrom-Json

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me