One more nice tool for the JsonValue API: a WPF control which can be used to view JsonValue graphs. It’s a simple control which inherits from TreeView and shows the graph in a tree-like fashion. This following JSON document, for example, is shown in the picture below.

[1,2,true,"hello world",null,{"one":1,"two":2,"bool":true,"str":"Hello world","array":[null,{},true,false]}]

                    JsonViewer

JsonValueViewer

The viewer uses the deep-event propagation I posted before to be notified of changes in the graph – when any place in the graph is changed, the viewer redraws the tree (ideally we’d only change the parts of the tree which have been changed, but I wanted to keep the code simple). Also, I wrote it in WPF, but I think it can be easily ported into Silverlight, as I didn't use any desktop-only feature. I've also played in changing the "icons" (A), (O) and (P) representing the JSON type (Array, Object, Primitive) to images, but that made the code longer (needed to either create the images on the fly or add images to the project resource).

  1. namespace JsonValueControls
  2. {
  3.     /// <summary>
  4.     /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
  5.     ///
  6.     /// Step 1a) Using this custom control in a XAML file that exists in the current project.
  7.     /// Add this XmlNamespace attribute to the root element of the markup file where it is
  8.     /// to be used:
  9.     ///
  10.     ///     xmlns:MyNamespace="clr-namespace:JsonValueControls"
  11.     ///
  12.     ///
  13.     /// Step 1b) Using this custom control in a XAML file that exists in a different project.
  14.     /// Add this XmlNamespace attribute to the root element of the markup file where it is
  15.     /// to be used:
  16.     ///
  17.     ///     xmlns:MyNamespace="clr-namespace:JsonValueControls;assembly=JsonValueControls"
  18.     ///
  19.     /// You will also need to add a project reference from the project where the XAML file lives
  20.     /// to this project and Rebuild to avoid compilation errors:
  21.     ///
  22.     ///     Right click on the target project in the Solution Explorer and
  23.     ///     "Add Reference"->"Projects"->[Select this project]
  24.     ///
  25.     ///
  26.     /// Step 2)
  27.     /// Go ahead and use your control in the XAML file.
  28.     ///
  29.     ///     <MyNamespace:JsonValueViewer/>
  30.     ///
  31.     /// </summary>
  32.     public class JsonValueViewer : TreeView
  33.     {
  34.         /// <summary>
  35.         /// Defines a property for binding a <see cref="JsonValue"/> value to this control.
  36.         /// </summary>
  37.         public static DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(JsonValue), typeof(JsonValueViewer));
  38.  
  39.         private JsonValue source;
  40.  
  41.         /// <summary>
  42.         /// Gets or sets the source for this viewer.
  43.         /// </summary>
  44.         public JsonValue Source
  45.         {
  46.             get
  47.             {
  48.                 return (JsonValue)this.GetValue(SourceProperty);
  49.             }
  50.  
  51.             set
  52.             {
  53.                 this.source = value;
  54.                 this.UpdateTree();
  55.                 this.SetValue(SourceProperty, value);
  56.             }
  57.         }
  58.  
  59.         private static bool IsComplex(JsonValue jsonValue)
  60.         {
  61.             return (jsonValue != null && jsonValue.JsonType != JsonType.Boolean && jsonValue.JsonType != JsonType.Number && jsonValue.JsonType != JsonType.String);
  62.         }
  63.  
  64.         private void UpdateTree()
  65.         {
  66.             this.Items.Clear();
  67.             JsonValueWrapper jsonWrapper = JsonValueWrapper.WrapJsonValue(null, null, this.source);
  68.             jsonWrapper.DeepChanged += new EventHandler<DeepJsonValueEventArgs>(jsonWrapper_DeepChanged);
  69.             TreeViewItem root = CreateNode(null, this.source, true);
  70.             this.Items.Add(root);
  71.             AddChildrenNodes(root, this.source);
  72.         }
  73.  
  74.         private void jsonWrapper_DeepChanged(object sender, DeepJsonValueEventArgs e)
  75.         {
  76.             (sender as JsonValueWrapper).Dispose();
  77.             this.UpdateTree();
  78.         }
  79.  
  80.         private void AddChildrenNodes(TreeViewItem root, JsonValue jsonValue)
  81.         {
  82.             if (jsonValue != null && IsComplex(jsonValue))
  83.             {
  84.                 bool isArray = jsonValue.JsonType == JsonType.Array;
  85.                 foreach (var child in jsonValue)
  86.                 {
  87.                     string format = isArray ? "[{0}]" : "\"{0}\"";
  88.                     string keyName = string.Format(CultureInfo.InvariantCulture, format, child.Key);
  89.                     TreeViewItem node = CreateNode(keyName, child.Value, true);
  90.                     root.Items.Add(node);
  91.                     AddChildrenNodes(node, child.Value);
  92.                 }
  93.             }
  94.         }
  95.  
  96.         private TreeViewItem CreateNode(string key, JsonValue value, bool isExpanded)
  97.         {
  98.             TreeViewItem result = new TreeViewItem();
  99.             result.IsExpanded = isExpanded;
  100.             StackPanel panel = new StackPanel { Orientation = Orientation.Horizontal };
  101.             string name = null;
  102.             bool addValue = value == null || !IsComplex(value);
  103.             string type = null;
  104.             if (value == null)
  105.             {
  106.                 type = "(P)";
  107.                 name = " <<null>>";
  108.             }
  109.             else
  110.             {
  111.                 switch (value.JsonType)
  112.                 {
  113.                     case JsonType.Object:
  114.                         type = "(O)";
  115.                         break;
  116.                     case JsonType.Array:
  117.                         type = "(A)";
  118.                         break;
  119.                     default:
  120.                         type = "(P)";
  121.                         name = " " + value.ToString();
  122.                         break;
  123.                 }
  124.             }
  125.  
  126.             panel.Children.Add(new TextBlock { Text = type, Foreground = Brushes.Red });
  127.             if (key != null)
  128.             {
  129.                 panel.Children.Add(new TextBlock { Text = " " + key });
  130.             }
  131.  
  132.             if (addValue)
  133.             {
  134.                 panel.Children.Add(new TextBlock { Text = " :" });
  135.                 if (name != null)
  136.                 {
  137.                     panel.Children.Add(new TextBlock { Text = name });
  138.                 }
  139.             }
  140.  
  141.             result.Header = panel;
  142.             return result;
  143.         }
  144.     }
  145. }

The source code for this project, for the wrapper and for a sample WPF project which uses the control can be found here.