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

Commit 2920ec8

Browse files
committed
Add support for Stripe-style nested complex type url encoded properties
1 parent a1e38b8 commit 2920ec8

File tree

3 files changed

+840
-197
lines changed

3 files changed

+840
-197
lines changed

src/ServiceStack.Text/Common/WriteType.cs

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Reflection;
1818
using ServiceStack.Reflection;
1919
using ServiceStack.Text.Json;
20+
using ServiceStack.Text.Jsv;
2021

2122
namespace ServiceStack.Text.Common
2223
{
@@ -178,6 +179,7 @@ private static bool Init()
178179

179180
PropertyWriters[i] = new TypePropertyWriter
180181
(
182+
propertyType,
181183
propertyName,
182184
propertyDeclaredTypeName,
183185
propertyNameCLSFriendly,
@@ -234,6 +236,7 @@ private static bool Init()
234236

235237
PropertyWriters[i + propertyNamesLength] = new TypePropertyWriter
236238
(
239+
propertyType,
237240
propertyName,
238241
propertyDeclaredTypeName,
239242
propertyNameCLSFriendly,
@@ -267,6 +270,8 @@ internal string PropertyName
267270
: propertyName;
268271
}
269272
}
273+
274+
internal readonly Type PropertyType;
270275
internal readonly string propertyName;
271276
internal readonly int propertyOrder;
272277
internal readonly bool propertySuppressDefaultConfig;
@@ -281,13 +286,14 @@ internal string PropertyName
281286
internal readonly Func<T, string, bool?> shouldSerializeDynamic;
282287
internal readonly bool isEnum;
283288

284-
public TypePropertyWriter(string propertyName, string propertyDeclaredTypeName, string propertyNameCLSFriendly,
289+
public TypePropertyWriter(Type propertyType, string propertyName, string propertyDeclaredTypeName, string propertyNameCLSFriendly,
285290
string propertyNameLowercaseUnderscore, int propertyOrder, bool propertySuppressDefaultConfig, bool propertySuppressDefaultAttribute,
286291
Func<T, object> getterFn, WriteObjectDelegate writeFn, object defaultValue,
287292
Func<T, bool> shouldSerialize,
288293
Func<T, string, bool?> shouldSerializeDynamic,
289294
bool isEnum)
290295
{
296+
this.PropertyType = propertyType;
291297
this.propertyName = propertyName;
292298
this.propertyOrder = propertyOrder;
293299
this.propertySuppressDefaultConfig = propertySuppressDefaultConfig;
@@ -359,6 +365,15 @@ public static void WriteAbstractProperties(TextWriter writer, object value)
359365
WriteLateboundProperties(writer, value, valueType);
360366
}
361367

368+
internal static string GetPropertyName(string propertyName)
369+
{
370+
return JsConfig<T>.EmitCamelCaseNames.GetValueOrDefault(JsConfig.EmitCamelCaseNames)
371+
? propertyName.ToCamelCase()
372+
: JsConfig<T>.EmitLowercaseUnderscoreNames.GetValueOrDefault(JsConfig.EmitLowercaseUnderscoreNames)
373+
? propertyName.ToLowercaseUnderscore()
374+
: propertyName;
375+
}
376+
362377
public static void WriteProperties(TextWriter writer, object value)
363378
{
364379
if (value == null)
@@ -488,20 +503,54 @@ public static void WriteComplexQueryStringProperties(string typeName, TextWriter
488503
if (i++ > 0)
489504
writer.Write('&');
490505

491-
writer.Write(typeName);
492-
writer.Write('[');
493-
writer.Write(propertyWriter.PropertyName);
494-
writer.Write(']');
506+
var propertyValueType = propertyValue != null ? propertyValue.GetType() : null;
507+
if (propertyValueType != null &&
508+
propertyValueType.IsUserType() &&
509+
!propertyValueType.HasInterface(typeof(IEnumerable)))
510+
{
511+
//Nested Complex Type: legal_entity[dob][day]=1
512+
var prefix = "{0}[{1}]".Fmt(typeName, propertyWriter.PropertyName);
513+
var props = propertyValueType.GetSerializableProperties();
514+
for (int j = 0; j < props.Length; j++)
515+
{
516+
var pi = props[j];
517+
var pValue = pi.GetValue(propertyValue, new object[0]);
518+
if (pValue == null && !Serializer.IncludeNullValues)
519+
continue;
495520

496-
writer.Write('=');
521+
if (j > 0)
522+
writer.Write('&');
497523

498-
if (propertyValue == null)
499-
{
500-
writer.Write(JsonUtils.Null);
524+
writer.Write(prefix);
525+
writer.Write('[');
526+
writer.Write(GetPropertyName(pi.Name));
527+
writer.Write("]=");
528+
529+
if (pValue == null)
530+
{
531+
writer.Write(JsonUtils.Null);
532+
}
533+
else
534+
{
535+
JsvWriter.GetWriteFn(pValue.GetType())(writer, pValue);
536+
}
537+
}
501538
}
502539
else
503540
{
504-
propertyWriter.WriteFn(writer, propertyValue);
541+
writer.Write(typeName);
542+
writer.Write('[');
543+
writer.Write(propertyWriter.PropertyName);
544+
writer.Write("]=");
545+
546+
if (propertyValue == null)
547+
{
548+
writer.Write(JsonUtils.Null);
549+
}
550+
else
551+
{
552+
propertyWriter.WriteFn(writer, propertyValue);
553+
}
505554
}
506555
}
507556
}
@@ -524,7 +573,7 @@ public static void WriteQueryString(TextWriter writer, object value)
524573
var propertyType = propertyValue.GetType();
525574
var strValue = propertyValue as string;
526575
var isEnumerable = strValue == null
527-
&& !(propertyType.IsValueType())
576+
&& !propertyType.IsValueType()
528577
&& propertyType.HasInterface(typeof(IEnumerable));
529578

530579
if (QueryStringSerializer.ComplexTypeStrategy != null

0 commit comments

Comments
 (0)