private/runtime/Writers/JsonWriter.cs
using System; using System.Collections.Generic; using System.IO; using System.Text.Encodings.Web; namespace Carbon.Json { public class JsonWriter { const string indentation = " "; // 2 spaces private readonly bool pretty; private readonly TextWriter writer; protected int currentLevel = 0; public JsonWriter(TextWriter writer, bool pretty = true) { this.writer = writer ?? throw new ArgumentNullException(nameof(writer)); this.pretty = pretty; } public 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; } } public 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(']'); } public void WriteIndent() { if (pretty) { writer.Write(Environment.NewLine); for (int level = 0; level < currentLevel; level++) { writer.Write(indentation); } } } public 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('}'); } public void WriteFieldName(string fieldName) { writer.Write('"'); JavaScriptEncoder.Default.Encode(writer, fieldName); writer.Write('"'); } #region Primitives public void WriteBinary(XBinary value) { writer.Write('"'); writer.Write(value.ToString()); writer.Write('"'); } public void WriteBoolean(bool value) { writer.Write(value ? "true" : "false"); } public void WriteDate(JsonDate date) { if (date.ToDateTime().Year == 1) { WriteNull(); } else { writer.Write('"'); writer.Write(date.ToIsoString()); writer.Write('"'); } } public void WriteNull() { writer.Write("null"); } public void WriteNumber(JsonNumber number) { if (number.Overflows) { writer.Write('"'); writer.Write(number.Value); writer.Write('"'); } else { writer.Write(number.Value); } } public void WriteString(string text) { if (text == null) { WriteNull(); } else { writer.Write('"'); JavaScriptEncoder.Default.Encode(writer, text); writer.Write('"'); } } #endregion } } // TODO: Replace with System.Text.Json when available |