Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 0fb69a0

Browse files
committed
Add support for indented json via config/scoped json
1 parent 761a9b5 commit 0fb69a0

File tree

5 files changed

+61
-10
lines changed

5 files changed

+61
-10
lines changed

src/ServiceStack.Text/JsConfig.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ public static JsConfigScope CreateScope(string config, JsConfigScope scope = nul
9696
case "includetypeinfo":
9797
scope.IncludeTypeInfo = boolValue;
9898
break;
99+
case "i":
100+
case "pp": //pretty-print
101+
case "indent":
102+
scope.Indent = boolValue;
103+
break;
99104
case "eccn":
100105
case "emitcamelcasenames":
101106
scope.TextCase = boolValue ? TextCase.CamelCase : scope.TextCase;
@@ -399,6 +404,12 @@ public static bool IncludeTypeInfo
399404
set => Config.AssertNotInit().IncludeTypeInfo = value;
400405
}
401406

407+
public static bool Indent
408+
{
409+
get => JsConfigScope.Current != null ? JsConfigScope.Current.Indent : Config.Instance.Indent;
410+
set => Config.AssertNotInit().Indent = value;
411+
}
412+
402413
public static string TypeAttr
403414
{
404415
get => JsConfigScope.Current != null ? JsConfigScope.Current.TypeAttr : Config.Instance.TypeAttr;

src/ServiceStack.Text/JsConfigScope.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ private Config(Config config)
120120
public bool TreatEnumAsInteger { get; set; }
121121
public bool ExcludeTypeInfo { get; set; }
122122
public bool IncludeTypeInfo { get; set; }
123+
public bool Indent { get; set; }
123124

124125
private string typeAttr;
125126
public string TypeAttr
@@ -196,6 +197,7 @@ public bool EmitLowercaseUnderscoreNames
196197
TreatEnumAsInteger = false,
197198
ExcludeTypeInfo = false,
198199
IncludeTypeInfo = false,
200+
Indent = false,
199201
TypeAttr = JsWriter.TypeAttr,
200202
DateTimeFormat = null,
201203
TypeWriter = AssemblyUtils.WriteType,
@@ -240,6 +242,7 @@ public Config Populate(Config config)
240242
TreatEnumAsInteger = config.TreatEnumAsInteger;
241243
ExcludeTypeInfo = config.ExcludeTypeInfo;
242244
IncludeTypeInfo = config.IncludeTypeInfo;
245+
Indent = config.Indent;
243246
TypeAttr = config.TypeAttr;
244247
DateTimeFormat = config.DateTimeFormat;
245248
TypeWriter = config.TypeWriter;

src/ServiceStack.Text/Json/JsonWriter.Generic.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,17 @@ public static void WriteObject(TextWriter writer, object value)
214214
}
215215

216216
public static void WriteRootObject(TextWriter writer, object value)
217+
{
218+
GetRootObjectWriteFn(value)(writer, value);
219+
}
220+
221+
public static WriteObjectDelegate GetRootObjectWriteFn(object value)
217222
{
218223
TypeConfig<T>.Init();
219224
JsonSerializer.OnSerialize?.Invoke(value);
220225

221226
JsState.Depth = 0;
222-
CacheFn(writer, value);
227+
return CacheFn;
223228
}
224229
}
225230

src/ServiceStack.Text/JsonSerializer.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public static string SerializeToString<T>(T value)
9999
}
100100
else
101101
{
102-
JsonWriter<T>.WriteRootObject(writer, value);
102+
WriteObjectToWriter(value, JsonWriter<T>.GetRootObjectWriteFn(value), writer);
103103
}
104104
return StringWriterThreadStatic.ReturnAndFree(writer);
105105
}
@@ -116,7 +116,7 @@ public static string SerializeToString(object value, Type type)
116116
else
117117
{
118118
OnSerialize?.Invoke(value);
119-
JsonWriter.GetWriteFn(type)(writer, value);
119+
WriteObjectToWriter(value, JsonWriter.GetWriteFn(type), writer);
120120
}
121121
return StringWriterThreadStatic.ReturnAndFree(writer);
122122
}
@@ -141,7 +141,7 @@ public static void SerializeToWriter<T>(T value, TextWriter writer)
141141
}
142142
else
143143
{
144-
JsonWriter<T>.WriteRootObject(writer, value);
144+
WriteObjectToWriter(value, JsonWriter<T>.GetRootObjectWriteFn(value), writer);
145145
}
146146
}
147147

@@ -155,7 +155,7 @@ public static void SerializeToWriter(object value, Type type, TextWriter writer)
155155
}
156156

157157
OnSerialize?.Invoke(value);
158-
JsonWriter.GetWriteFn(type)(writer, value);
158+
WriteObjectToWriter(value, JsonWriter.GetWriteFn(type), writer);
159159
}
160160

161161
public static void SerializeToStream<T>(T value, Stream stream)
@@ -175,7 +175,7 @@ public static void SerializeToStream<T>(T value, Stream stream)
175175
else
176176
{
177177
var writer = new StreamWriter(stream, JsConfig.UTF8Encoding, BufferSize, leaveOpen:true);
178-
JsonWriter<T>.WriteRootObject(writer, value);
178+
WriteObjectToWriter(value, JsonWriter<T>.GetRootObjectWriteFn(value), writer);
179179
writer.Flush();
180180
}
181181
}
@@ -184,10 +184,27 @@ public static void SerializeToStream(object value, Type type, Stream stream)
184184
{
185185
OnSerialize?.Invoke(value);
186186
var writer = new StreamWriter(stream, JsConfig.UTF8Encoding, BufferSize, leaveOpen:true);
187-
JsonWriter.GetWriteFn(type)(writer, value);
187+
WriteObjectToWriter(value, JsonWriter.GetWriteFn(type), writer);
188188
writer.Flush();
189189
}
190190

191+
private static void WriteObjectToWriter(object value, WriteObjectDelegate serializeFn, TextWriter writer)
192+
{
193+
if (!JsConfig.Indent)
194+
{
195+
serializeFn(writer, value);
196+
}
197+
else
198+
{
199+
var sb = StringBuilderCache.Allocate();
200+
using var captureJson = new StringWriter(sb);
201+
serializeFn(captureJson, value);
202+
captureJson.Flush();
203+
var json = StringBuilderCache.ReturnAndFree(sb);
204+
var indentJson = json.IndentJson();
205+
writer.Write(indentJson);
206+
}
207+
}
191208
public static T DeserializeFromStream<T>(Stream stream)
192209
{
193210
return (T)MemoryProvider.Instance.Deserialize(stream, typeof(T), DeserializeFromSpan);

tests/ServiceStack.Text.Tests/JsConfigTests.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,19 @@ class TestObject
201201
}
202202

203203
private const string AssertMessageFormat = "Cannot find correct property value ({0})";
204+
205+
[Test]
206+
public void Can_indent_in_scoped_json()
207+
{
208+
var obj = new TestObject { Id = 1 };
209+
using (JsConfig.With(new Config { Indent = true }))
210+
{
211+
var scopedJson = obj.ToJson();
212+
Assert.That(scopedJson.NormalizeNewLines(), Is.EqualTo("{\n \"Id\": 1,\n \"RootId\": 0\n}"));
213+
}
214+
var json = obj.ToJson();
215+
Assert.That(json, Is.EqualTo("{\"Id\":1,\"RootId\":0}"));
216+
}
204217
}
205218

206219
[TestFixture]
@@ -209,11 +222,12 @@ public class JsConfigCreateTests
209222
[Test]
210223
public void Does_create_scope_from_string()
211224
{
212-
var scope = JsConfig.CreateScope("emitlowercaseunderscorenames,IncludeNullValues:false,ExcludeDefaultValues:0,IncludeDefaultEnums:1");
225+
var scope = JsConfig.CreateScope("emitlowercaseunderscorenames,IncludeNullValues:false,ExcludeDefaultValues:0,IncludeDefaultEnums:1,indent");
213226
Assert.That(scope.TextCase, Is.EqualTo(TextCase.SnakeCase));
214227
Assert.That(!scope.IncludeNullValues);
215228
Assert.That(!scope.ExcludeDefaultValues);
216229
Assert.That(scope.IncludeDefaultEnums);
230+
Assert.That(scope.Indent);
217231
scope.Dispose();
218232

219233
scope = JsConfig.CreateScope("DateHandler:ISO8601,timespanhandler:durationformat,PropertyConvention:strict,TextCase:CamelCase");
@@ -227,11 +241,12 @@ public void Does_create_scope_from_string()
227241
[Test]
228242
public void Does_create_scope_from_string_using_CamelCaseHumps()
229243
{
230-
var scope = JsConfig.CreateScope("eccn,inv:false,edv:0,ide:1");
244+
var scope = JsConfig.CreateScope("eccn,inv:false,edv:0,ide:1,pp");
231245
Assert.That(scope.TextCase, Is.EqualTo(TextCase.CamelCase));
232246
Assert.That(!scope.IncludeNullValues);
233247
Assert.That(!scope.ExcludeDefaultValues);
234248
Assert.That(scope.IncludeDefaultEnums);
249+
Assert.That(scope.Indent);
235250
scope.Dispose();
236251

237252
scope = JsConfig.CreateScope("dh:ISO8601,tsh:df,pc:strict,tc:cc");
@@ -274,7 +289,7 @@ public void Does_not_allow_setting_multiple_inits_in_StrictMode()
274289

275290
Env.StrictMode = true;
276291

277-
Assert.Throws<NotSupportedException>(() => JsConfig.Init());
292+
Assert.Throws<NotSupportedException>(JsConfig.Init);
278293
}
279294

280295
[Test]

0 commit comments

Comments
 (0)