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

Commit c0ad2cc

Browse files
committed
Fix QueryString serialization by buffering instead of multi-checks in lists serializers
1 parent 59208ab commit c0ad2cc

File tree

4 files changed

+91
-25
lines changed

4 files changed

+91
-25
lines changed

src/ServiceStack.Text/Common/WriteLists.cs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public static void WriteList(TextWriter writer, object oList)
210210

211211
public static void WriteGenericList(TextWriter writer, List<T> list)
212212
{
213-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
213+
writer.Write(JsWriter.ListStartChar);
214214

215215
var ranOnce = false;
216216
var listLength = list.Count;
@@ -220,7 +220,7 @@ public static void WriteGenericList(TextWriter writer, List<T> list)
220220
ElementWriteFn(writer, list[i]);
221221
}
222222

223-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
223+
writer.Write(JsWriter.ListEndChar);
224224
}
225225

226226
public static void WriteListValueType(TextWriter writer, object list)
@@ -232,7 +232,7 @@ public static void WriteGenericListValueType(TextWriter writer, List<T> list)
232232
{
233233
if (list == null) return; //AOT
234234

235-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
235+
writer.Write(JsWriter.ListStartChar);
236236

237237
var ranOnce = false;
238238
var listLength = list.Count;
@@ -242,7 +242,7 @@ public static void WriteGenericListValueType(TextWriter writer, List<T> list)
242242
ElementWriteFn(writer, list[i]);
243243
}
244244

245-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
245+
writer.Write(JsWriter.ListEndChar);
246246
}
247247

248248
public static void WriteIList(TextWriter writer, object oList)
@@ -253,7 +253,7 @@ public static void WriteIList(TextWriter writer, object oList)
253253
public static void WriteGenericIList(TextWriter writer, IList<T> list)
254254
{
255255
if (list == null) return;
256-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
256+
writer.Write(JsWriter.ListStartChar);
257257

258258
var ranOnce = false;
259259
var listLength = list.Count;
@@ -271,7 +271,7 @@ public static void WriteGenericIList(TextWriter writer, IList<T> list)
271271
Tracer.Instance.WriteError(ex);
272272
throw;
273273
}
274-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
274+
writer.Write(JsWriter.ListEndChar);
275275
}
276276

277277
public static void WriteIListValueType(TextWriter writer, object list)
@@ -283,7 +283,7 @@ public static void WriteGenericIListValueType(TextWriter writer, IList<T> list)
283283
{
284284
if (list == null) return; //AOT
285285

286-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
286+
writer.Write(JsWriter.ListStartChar);
287287

288288
var ranOnce = false;
289289
var listLength = list.Count;
@@ -293,7 +293,7 @@ public static void WriteGenericIListValueType(TextWriter writer, IList<T> list)
293293
ElementWriteFn(writer, list[i]);
294294
}
295295

296-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
296+
writer.Write(JsWriter.ListEndChar);
297297
}
298298

299299
public static void WriteArray(TextWriter writer, object oArrayValue)
@@ -310,7 +310,7 @@ public static void WriteGenericArrayValueType(TextWriter writer, object oArray)
310310
public static void WriteGenericArrayValueType(TextWriter writer, T[] array)
311311
{
312312
if (array == null) return;
313-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
313+
writer.Write(JsWriter.ListStartChar);
314314

315315
var ranOnce = false;
316316
var arrayLength = array.Length;
@@ -320,13 +320,13 @@ public static void WriteGenericArrayValueType(TextWriter writer, T[] array)
320320
ElementWriteFn(writer, array[i]);
321321
}
322322

323-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
323+
writer.Write(JsWriter.ListEndChar);
324324
}
325325

326326
private static void WriteGenericArrayMultiDimension(TextWriter writer, Array array, int rank, int[] indices)
327327
{
328328
var ranOnce = false;
329-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
329+
writer.Write(JsWriter.ListStartChar);
330330
for (int i = 0; i < array.GetLength(rank); i++)
331331
{
332332
JsWriter.WriteItemSeperatorIfRanOnce(writer, ref ranOnce);
@@ -337,7 +337,7 @@ private static void WriteGenericArrayMultiDimension(TextWriter writer, Array arr
337337
else
338338
ElementWriteFn(writer, array.GetValue(indices));
339339
}
340-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
340+
writer.Write(JsWriter.ListEndChar);
341341
}
342342

343343
public static void WriteGenericArray(TextWriter writer, Array array)
@@ -352,7 +352,7 @@ public static void WriteEnumerable(TextWriter writer, object oEnumerable)
352352
public static void WriteGenericEnumerable(TextWriter writer, IEnumerable<T> enumerable)
353353
{
354354
if (enumerable == null) return;
355-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
355+
writer.Write(JsWriter.ListStartChar);
356356

357357
var ranOnce = false;
358358
foreach (var value in enumerable)
@@ -361,12 +361,12 @@ public static void WriteGenericEnumerable(TextWriter writer, IEnumerable<T> enum
361361
ElementWriteFn(writer, value);
362362
}
363363

364-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
364+
writer.Write(JsWriter.ListEndChar);
365365
}
366366

367367
public static void WriteGenericEnumerableValueType(TextWriter writer, IEnumerable<T> enumerable)
368368
{
369-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
369+
writer.Write(JsWriter.ListStartChar);
370370

371371
var ranOnce = false;
372372
foreach (var value in enumerable)
@@ -375,7 +375,7 @@ public static void WriteGenericEnumerableValueType(TextWriter writer, IEnumerabl
375375
ElementWriteFn(writer, value);
376376
}
377377

378-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
378+
writer.Write(JsWriter.ListEndChar);
379379
}
380380
}
381381

@@ -388,7 +388,7 @@ public static void WriteListString(ITypeSerializer serializer, TextWriter writer
388388

389389
public static void WriteListString(ITypeSerializer serializer, TextWriter writer, List<string> list)
390390
{
391-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
391+
writer.Write(JsWriter.ListStartChar);
392392

393393
var ranOnce = false;
394394
foreach (var x in list)
@@ -397,7 +397,7 @@ public static void WriteListString(ITypeSerializer serializer, TextWriter writer
397397
serializer.WriteString(writer, x);
398398
}
399399

400-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
400+
writer.Write(JsWriter.ListEndChar);
401401
}
402402

403403
public static void WriteIListString(ITypeSerializer serializer, TextWriter writer, object list)
@@ -407,7 +407,7 @@ public static void WriteIListString(ITypeSerializer serializer, TextWriter write
407407

408408
public static void WriteIListString(ITypeSerializer serializer, TextWriter writer, IList<string> list)
409409
{
410-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
410+
writer.Write(JsWriter.ListStartChar);
411411

412412
var ranOnce = false;
413413
var listLength = list.Count;
@@ -417,7 +417,7 @@ public static void WriteIListString(ITypeSerializer serializer, TextWriter write
417417
serializer.WriteString(writer, list[i]);
418418
}
419419

420-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
420+
writer.Write(JsWriter.ListEndChar);
421421
}
422422

423423
public static void WriteBytes(ITypeSerializer serializer, TextWriter writer, object byteValue)
@@ -428,7 +428,7 @@ public static void WriteBytes(ITypeSerializer serializer, TextWriter writer, obj
428428

429429
public static void WriteStringArray(ITypeSerializer serializer, TextWriter writer, object oList)
430430
{
431-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListStartChar);
431+
writer.Write(JsWriter.ListStartChar);
432432

433433
var list = (string[])oList;
434434
var ranOnce = false;
@@ -439,7 +439,7 @@ public static void WriteStringArray(ITypeSerializer serializer, TextWriter write
439439
serializer.WriteString(writer, list[i]);
440440
}
441441

442-
if (!JsState.QueryStringMode) writer.Write(JsWriter.ListEndChar);
442+
writer.Write(JsWriter.ListEndChar);
443443
}
444444
}
445445

src/ServiceStack.Text/Common/WriteType.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ public static void WriteProperties(TextWriter writer, object value)
310310
writer.Write(JsWriter.QuoteChar);
311311
}
312312

313+
private static readonly char[] ArrayBrackets = new[] { '[', ']' };
314+
313315
public static void WriteQueryString(TextWriter writer, object value)
314316
{
315317
try
@@ -326,7 +328,29 @@ public static void WriteQueryString(TextWriter writer, object value)
326328

327329
Serializer.WritePropertyName(writer, propertyWriter.PropertyName);
328330
writer.Write('=');
329-
propertyWriter.WriteFn(writer, propertyValue);
331+
332+
var isEnumerable = propertyValue != null
333+
&& !(propertyValue is string)
334+
&& !(propertyValue.GetType().IsValueType)
335+
&& propertyValue.GetType().HasInterface(typeof(IEnumerable));
336+
337+
if (!isEnumerable)
338+
{
339+
propertyWriter.WriteFn(writer, propertyValue);
340+
}
341+
else
342+
{
343+
//Trim brackets in top-level lists in QueryStrings, e.g: ?a=[1,2,3] => ?a=1,2,3
344+
using (var ms = new MemoryStream())
345+
using (var enumerableWriter = new StreamWriter(ms))
346+
{
347+
propertyWriter.WriteFn(enumerableWriter, propertyValue);
348+
enumerableWriter.Flush();
349+
var output = ms.ToArray().FromUtf8Bytes();
350+
output = output.Trim(ArrayBrackets);
351+
writer.Write(output);
352+
}
353+
}
330354
}
331355
}
332356
finally

src/ServiceStack.Text/QueryStringSerializer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ static QueryStringWriter()
112112
}
113113
else
114114
{
115-
if (typeof(T).IsClass() || typeof(T).IsInterface())
115+
var isEnumerable = typeof(T).AssignableFrom(typeof(IEnumerable))
116+
|| typeof(T).HasInterface(typeof(IEnumerable));
117+
118+
if ((typeof(T).IsClass() || typeof(T).IsInterface())
119+
&& !isEnumerable)
116120
{
117121
var canWriteType = WriteType<T, JsvTypeSerializer>.Write;
118122
if (canWriteType != null)

tests/ServiceStack.Text.Tests/QueryStringSerializerTests.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,43 @@ public void Can_deserialize_query_string_nullableInt_NaN_throws()
100100
{
101101
Assert.Throws(typeof(FormatException), delegate { ServiceStack.Text.Common.DeserializeBuiltin<int?>.Parse("NaN"); });
102102
}
103-
}
103+
104+
[Test]
105+
public void Deos_serialize_QueryStrings()
106+
{
107+
var testPocos = new TestPocos { ListOfA = new List<A> { new A { ListOfB = new List<B> { new B { Property = "prop1" }, new B { Property = "prop2" } } } } };
108+
109+
Assert.That(QueryStringSerializer.SerializeToString(testPocos), Is.EqualTo(
110+
"ListOfA={ListOfB:[{Property:prop1},{Property:prop2}]}"));
111+
112+
Assert.That(QueryStringSerializer.SerializeToString(new[] { 1, 2, 3 }), Is.EqualTo(
113+
"[1,2,3]"));
114+
115+
Assert.That(QueryStringSerializer.SerializeToString(new[] { "AA", "BB", "CC" }), Is.EqualTo(
116+
"[AA,BB,CC]"));
117+
}
118+
119+
public class TestService : ServiceInterface.Service
120+
{
121+
public object Get(TestPocos request)
122+
{
123+
return "OK";
124+
}
125+
}
126+
127+
public class TestPocos
128+
{
129+
public List<A> ListOfA { get; set; }
130+
}
131+
132+
public class A
133+
{
134+
public List<B> ListOfB { get; set; }
135+
}
136+
137+
public class B
138+
{
139+
public string Property { get; set; }
140+
}
141+
}
104142
}

0 commit comments

Comments
 (0)