Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Packages/Audience/Runtime/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
52 changes: 39 additions & 13 deletions src/Packages/Audience/Runtime/Utility/Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,23 @@ internal static class Json
internal static string Serialize(Dictionary<string, object> 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<string, object> 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)
{
Expand Down Expand Up @@ -56,46 +68,60 @@ private static void WriteValue(StringBuilder sb, object value)
}
else if (value is Dictionary<string, object> dict)
{
WriteObject(sb, dict);
WriteObject(sb, dict, indent, depth);
}
else if (value is IList list)
{
WriteArray(sb, list);
WriteArray(sb, list, indent, depth);
}
else
{
WriteString(sb, value.ToString());
}
}

private static void WriteObject(StringBuilder sb, Dictionary<string, object> dict)
private static void WriteObject(StringBuilder sb, Dictionary<string, object> 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('"');
Expand Down
8 changes: 8 additions & 0 deletions src/Packages/Audience/Tests/Audience.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,12 @@
<ItemGroup>
<ProjectReference Include="../Runtime/Audience.Runtime.csproj" />
</ItemGroup>
<!--
Exclude Tests/Editor/ from the headless dotnet build. Those tests reference
Unity-only types (MonoBehaviour, VisualElement, SampleApp MonoBehaviour)
and run inside the Unity Test Framework in-editor.
-->
<ItemGroup>
<Compile Remove="Editor/**/*.cs" />
</ItemGroup>
</Project>
Loading