diff --git a/src/Packages/Audience/Runtime/AssemblyInfo.cs b/src/Packages/Audience/Runtime/AssemblyInfo.cs index 5f1a3ea0..d390acb9 100644 --- a/src/Packages/Audience/Runtime/AssemblyInfo.cs +++ b/src/Packages/Audience/Runtime/AssemblyInfo.cs @@ -2,3 +2,8 @@ [assembly: InternalsVisibleTo("Immutable.Audience.Runtime.Tests")] [assembly: InternalsVisibleTo("Immutable.Audience.Unity")] + +// First-party SampleApp reaches Json.Serialize and +// JsonReader.DeserializeObject; both stay internal so their +// signatures aren't frozen into the public API. +[assembly: InternalsVisibleTo("Immutable.Audience.Samples.SampleApp")] diff --git a/src/Packages/Audience/Runtime/Utility/Json.cs b/src/Packages/Audience/Runtime/Utility/Json.cs index b14e1c0d..7868a027 100644 --- a/src/Packages/Audience/Runtime/Utility/Json.cs +++ b/src/Packages/Audience/Runtime/Utility/Json.cs @@ -10,11 +10,23 @@ internal static class Json internal static string Serialize(Dictionary data) { var sb = new StringBuilder(); - WriteObject(sb, data); + WriteObject(sb, data, indent: 0, depth: 0); return sb.ToString(); } - private static void WriteValue(StringBuilder sb, object value) + // Pretty-prints `data` with `indent` spaces per nesting level. + // Round-trips through Deserialize identically. Indent ≤ 0 returns + // the compact form. Use for human-readable output; wire payloads + // use the compact overload. + internal static string Serialize(Dictionary data, int indent) + { + if (indent <= 0) return Serialize(data); + var sb = new StringBuilder(); + WriteObject(sb, data, indent, depth: 0); + return sb.ToString(); + } + + private static void WriteValue(StringBuilder sb, object value, int indent, int depth) { if (value == null) { @@ -56,11 +68,11 @@ private static void WriteValue(StringBuilder sb, object value) } else if (value is Dictionary dict) { - WriteObject(sb, dict); + WriteObject(sb, dict, indent, depth); } else if (value is IList list) { - WriteArray(sb, list); + WriteArray(sb, list, indent, depth); } else { @@ -68,34 +80,48 @@ private static void WriteValue(StringBuilder sb, object value) } } - private static void WriteObject(StringBuilder sb, Dictionary dict) + private static void WriteObject(StringBuilder sb, Dictionary dict, int indent, int depth) { sb.Append('{'); + if (dict.Count == 0) { sb.Append('}'); return; } + + var pretty = indent > 0; var first = true; foreach (var kvp in dict) { - if (!first) - sb.Append(','); + if (!first) sb.Append(','); first = false; + if (pretty) AppendNewline(sb, indent, depth + 1); WriteString(sb, kvp.Key); - sb.Append(':'); - WriteValue(sb, kvp.Value); + sb.Append(pretty ? ": " : ":"); + WriteValue(sb, kvp.Value, indent, depth + 1); } + if (pretty) AppendNewline(sb, indent, depth); sb.Append('}'); } - private static void WriteArray(StringBuilder sb, IList list) + private static void WriteArray(StringBuilder sb, IList list, int indent, int depth) { sb.Append('['); + if (list.Count == 0) { sb.Append(']'); return; } + + var pretty = indent > 0; for (var i = 0; i < list.Count; i++) { - if (i > 0) - sb.Append(','); - WriteValue(sb, list[i]); + if (i > 0) sb.Append(','); + if (pretty) AppendNewline(sb, indent, depth + 1); + WriteValue(sb, list[i], indent, depth + 1); } + if (pretty) AppendNewline(sb, indent, depth); sb.Append(']'); } + private static void AppendNewline(StringBuilder sb, int indent, int depth) + { + sb.Append('\n'); + sb.Append(' ', indent * depth); + } + private static void WriteString(StringBuilder sb, string s) { sb.Append('"'); diff --git a/src/Packages/Audience/Tests/Audience.Tests.csproj b/src/Packages/Audience/Tests/Audience.Tests.csproj index a4bc2e18..0194901a 100644 --- a/src/Packages/Audience/Tests/Audience.Tests.csproj +++ b/src/Packages/Audience/Tests/Audience.Tests.csproj @@ -15,4 +15,12 @@ + + + +