Friday, January 13, 2012

C# JSon Pretty-Print (A simple C# JSon Presentation Formatter)

When working on a recent project, I ran across a need to have the user paste into a textbox (in a WPF application) a snippet of  unformatted  JSon data. The data was then used with a template to render content for them to view. The hard part... the unformatted JSon data was almost impossible to read (let alone do anything with).

A quick search around on Google, and I couldn't find what I thought was pretty basic function in C#. I mean, someone, somewhere else in the world needed to be able to do this. The Javascript serializer would work if I had an object to start with, but in my case, there wasn't an object just a string. So I wrote a basic formatter for presentation of JSon data. It's not all that impressive, but it works, and creates presentable data that the user can view and work with.

I also added a quick method as well that would convert an object to a string, and then apply the same formatting algorithm in-case you have a need to present both and want to have the same presentation.

So instead of this:



You get this:












The Code:
        public class JSonPresentationFormatter
        {
            public string Format(object obj)
            {
                var serializer = new JavaScriptSerializer();
                var text = serializer.Serialize(obj);

                return Format(text);
            }

            public string Format(string text)
            {
                if (string.IsNullOrEmpty(text)) return string.Empty;
                text = text.Replace(System.Environment.NewLine, string.Empty).Replace("\t", string.Empty);

                var offset = 0;
                var output = new StringBuilder();
                Action tabs = (sb, pos) => { for (var i = 0; i < pos; i++) { sb.Append("\t"); } };
                Func> previousNotEmpty = (s, i) =>
                {
                    if (string.IsNullOrEmpty(s) || i <= 0) return null;

                    Nullable prev = null;

                    while (i > 0 && prev == null)
                    {
                        prev = s[i - 1];
                        if (prev.ToString() == " ") prev = null;
                        i--;
                    }

                    return prev;
                };
                Func> nextNotEmpty = (s, i) =>
                {
                    if (string.IsNullOrEmpty(s) || i >= (s.Length - 1)) return null;

                    Nullable next = null;
                    i++;

                    while (i < (s.Length - 1) && next == null)
                    {
                        next = s[i++];
                        if (next.ToString() == " ") next = null;
                    }

                    return next;
                };

                for (var i = 0; i < text.Length; i++)
                {
                    var chr = text[i];

                    if (chr.ToString() == "{")
                    {
                        offset++;
                        output.Append(chr);
                        output.Append(System.Environment.NewLine);
                        tabs(output, offset);
                    }
                    else if (chr.ToString() == "}")
                    {
                        offset--;
                        output.Append(System.Environment.NewLine);
                        tabs(output, offset);
                        output.Append(chr);

                    }
                    else if (chr.ToString() == ",")
                    {
                        output.Append(chr);
                        output.Append(System.Environment.NewLine);
                        tabs(output, offset);
                    }
                    else if (chr.ToString() == "[")
                    {
                        output.Append(chr);

                        var next = nextNotEmpty(text, i);

                        if (next != null && next.ToString() != "]")
                        {
                            offset++;
                            output.Append(System.Environment.NewLine);
                            tabs(output, offset);
                        }
                    }
                    else if (chr.ToString() == "]")
                    {
                        var prev = previousNotEmpty(text, i);

                        if (prev != null && prev.ToString() != "[")
                        {
                            offset--;
                            output.Append(System.Environment.NewLine);
                            tabs(output, offset);
                        }

                        output.Append(chr);
                    }
                    else
                        output.Append(chr);
                }

                return output.ToString().Trim();
            }
        }
You can download a copy of the source here: JSonPresentationFormatter
Example:
            //From object
            var employees = new[]
            {
                 new {Name= "John Vansteen",ID="1", Age="35"}
                ,new {Name = "Joe Yankin", ID = "2", Age = "44" }
                ,new {Name = "Larry Yon", ID = "3", Age = "33" }
            };
            Console.Write(new JSonPresentationFormatter().Format(employees));

            //From string
            var jSonAsString = "{\"glossary\": {\"title\": \"example glossary\",\"GlossDiv\": {\"title\": \"S\",\"GlossList\": {\"GlossEntry\": {\"ID\": \"SGML\",\"SortAs\": \"SGML\",\"GlossTerm\": \"Standard Generalized Markup Language\",\"Acronym\": \"SGML\",\"Abbrev\": \"ISO 8879:1986\",\"GlossDef\": {\"para\": \"A meta-markup language, used to create markup languages such as DocBook.\",\"GlossSeeAlso\": [\"GML\", \"XML\"]},\"GlossSee\": \"markup\"}}}}}";
            Console.Write(new JSonPresentationFormatter().Format(jSonAsString));
Key Words:
JSon C# Presentation Formatter

References:
IFormatProvider Interface

Source:
JSonPresentationFormatter

4 comments:

fleurdelys said...

Hi, can you please check your code.
there are syntax errors.

Func> previousNotEmpty = (s, i) =>
{...};
does not exist

Christopher Hardwick said...
This comment has been removed by the author.
Christopher Hardwick said...

@fleurdelys - Thanks for the comment. Blogger is messing up the formatting. previousNotEmpty and nextNotEmpty are both anonymous functions, and are within the Format(string text) method.

It should be:
Func> previousNotEmpty
Func> nextNotEmpty

You can download the .cs file from here:
http://dl.dropbox.com/u/52219470/Source/JSonPresentationFormatter.cs

If I can help any more, please do not hesitate to ask.

Christopher Hardwick said...

@fleurdelys. Sorry.. it messed up the syntax in the comment as well! Just take a look at the file download. It has what your looking for.

JSonPresentationFormatter