generated/runtime/Writers/JsonWriter.cs

/*---------------------------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;

namespace Commvault.Powershell.Runtime.Json
{
    internal class JsonWriter
    {
        const string indentation = " "; // 2 spaces

        private readonly bool pretty;
        private readonly TextWriter writer;

        protected int currentLevel = 0;

        internal JsonWriter(TextWriter writer, bool pretty = true)
        {
            this.writer = writer ?? throw new ArgumentNullException(nameof(writer));
            this.pretty = pretty;
        }

        internal void WriteNode(JsonNode node)
        {
            switch (node.Type)
            {
                case JsonType.Array: WriteArray((IEnumerable<JsonNode>)node); break;
                case JsonType.Object: WriteObject((JsonObject)node); break;

                // Primitives
                case JsonType.Binary: WriteBinary((XBinary)node); break;
                case JsonType.Boolean: WriteBoolean((bool)node); break;
                case JsonType.Date: WriteDate((JsonDate)node); break;
                case JsonType.Null: WriteNull(); break;
                case JsonType.Number: WriteNumber((JsonNumber)node); break;
                case JsonType.String: WriteString(node); break;
            }
        }

        internal void WriteArray(IEnumerable<JsonNode> array)
        {
            currentLevel++;

            writer.Write('[');

            bool doIndentation = false;

            if (pretty)
            {
                foreach (var node in array)
                {
                    if (node.Type == JsonType.Object || node.Type == JsonType.Array)
                    {
                        doIndentation = true;

                        break;
                    }
                }
            }

            bool isFirst = true;

            foreach (JsonNode node in array)
            {
                if (!isFirst) writer.Write(',');

                if (doIndentation)
                {
                    WriteIndent();
                }
                else if (pretty)
                {
                    writer.Write(' ');
                }

                WriteNode(node);

                isFirst = false;
            }

            currentLevel--;

            if (doIndentation)
            {
                WriteIndent();
            }
            else if (pretty)
            {
                writer.Write(' ');
            }

            writer.Write(']');
        }

        internal void WriteIndent()
        {
            if (pretty)
            {
                writer.Write(Environment.NewLine);

                for (int level = 0; level < currentLevel; level++)
                {
                    writer.Write(indentation);
                }
            }
        }

        internal void WriteObject(JsonObject obj)
        {
            currentLevel++;

            writer.Write('{');

            bool isFirst = true;

            foreach (var field in obj)
            {
                if (!isFirst) writer.Write(',');

                WriteIndent();

                WriteFieldName(field.Key);

                writer.Write(':');

                if (pretty)
                {
                    writer.Write(' ');
                }

                // Write the field value
                WriteNode(field.Value);

                isFirst = false;
            }

            currentLevel--;

            WriteIndent();

            writer.Write('}');
        }

        internal void WriteFieldName(string fieldName)
        {
            writer.Write('"');
            writer.Write(HttpUtility.JavaScriptStringEncode(fieldName));
            writer.Write('"');
        }

        #region Primitives

        internal void WriteBinary(XBinary value)
        {
            writer.Write('"');
            writer.Write(value.ToString());
            writer.Write('"');
        }

        internal void WriteBoolean(bool value)
        {
            writer.Write(value ? "true" : "false");
        }

        internal void WriteDate(JsonDate date)
        {
            if (date.ToDateTime().Year == 1)
            {
                WriteNull();
            }
            else
            {
                writer.Write('"');
                writer.Write(date.ToIsoString());
                writer.Write('"');
            }
        }

        internal void WriteNull()
        {
            writer.Write("null");
        }

        internal void WriteNumber(JsonNumber number)
        {
            if (number.Overflows)
            {
                writer.Write('"');
                writer.Write(number.Value);
                writer.Write('"');
            }
            else
            {
                writer.Write(number.Value);
            }
        }

        internal void WriteString(string text)
        {
            if (text == null)
            {
                WriteNull();
            }
            else
            {
                writer.Write('"');

                writer.Write(HttpUtility.JavaScriptStringEncode(text));

                writer.Write('"');
            }
        }

        #endregion
    }
}


// TODO: Replace with System.Text.Json when available