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

Commit e0e44a3

Browse files
committed
Avoid StackOverflow's in JsConfig.SerializeFn/DeserializeFns
1 parent bddd482 commit e0e44a3

3 files changed

Lines changed: 87 additions & 14 deletions

File tree

src/ServiceStack.Text/Common/JsState.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ internal static class JsState
1818
[ThreadStatic]
1919
internal static bool QueryStringMode = false;
2020

21+
[ThreadStatic]
22+
internal static bool InSerializeFn = false;
23+
24+
[ThreadStatic]
25+
internal static bool InDeserializeFn = false;
26+
2127
[ThreadStatic]
2228
internal static int Depth = 0;
2329
}

src/ServiceStack.Text/JsConfig.cs

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ public static bool TreatValueAsRefType
748748
/// </summary>
749749
public static bool HasSerializeFn
750750
{
751-
get { return serializeFn != null || rawSerializeFn != null; }
751+
get { return !JsState.InSerializeFn && (serializeFn != null || rawSerializeFn != null); }
752752
}
753753

754754
/// <summary>
@@ -802,7 +802,7 @@ public static Action<T> OnSerializedFn
802802

803803
public static bool HasDeserializeFn
804804
{
805-
get { return DeSerializeFn != null || RawDeserializeFn != null; }
805+
get { return !JsState.InDeserializeFn && (DeSerializeFn != null || RawDeserializeFn != null); }
806806
}
807807

808808
private static Func<T, T> onDeserializedFn;
@@ -819,14 +819,30 @@ public static Func<T, T> OnDeserializedFn
819819

820820
public static void WriteFn<TSerializer>(TextWriter writer, object obj)
821821
{
822-
if (RawSerializeFn != null)
823-
{
824-
writer.Write(RawSerializeFn((T)obj));
825-
}
826-
else if (SerializeFn != null)
827-
{
828-
var serializer = JsWriter.GetTypeSerializer<TSerializer>();
829-
serializer.WriteString(writer, SerializeFn((T)obj));
822+
if (RawSerializeFn != null && !JsState.InSerializeFn)
823+
{
824+
JsState.InSerializeFn = true;
825+
try
826+
{
827+
writer.Write(RawSerializeFn((T)obj));
828+
}
829+
finally
830+
{
831+
JsState.InSerializeFn = false;
832+
}
833+
}
834+
else if (SerializeFn != null && !JsState.InSerializeFn)
835+
{
836+
JsState.InSerializeFn = true;
837+
try
838+
{
839+
var serializer = JsWriter.GetTypeSerializer<TSerializer>();
840+
serializer.WriteString(writer, SerializeFn((T)obj));
841+
}
842+
finally
843+
{
844+
JsState.InSerializeFn = false;
845+
}
830846
}
831847
else
832848
{
@@ -842,13 +858,34 @@ public static object ParseFn(string str)
842858

843859
internal static object ParseFn(ITypeSerializer serializer, string str)
844860
{
845-
if (RawDeserializeFn != null)
846-
{
847-
return RawDeserializeFn(str);
861+
if (RawDeserializeFn != null && !JsState.InDeserializeFn)
862+
{
863+
JsState.InDeserializeFn = true;
864+
try
865+
{
866+
return RawDeserializeFn(str);
867+
}
868+
finally
869+
{
870+
JsState.InDeserializeFn = false;
871+
}
872+
}
873+
else if (DeSerializeFn != null && !JsState.InDeserializeFn)
874+
{
875+
JsState.InDeserializeFn = true;
876+
try
877+
{
878+
return DeSerializeFn(serializer.UnescapeString(str));
879+
}
880+
finally
881+
{
882+
JsState.InDeserializeFn = false;
883+
}
848884
}
849885
else
850886
{
851-
return DeSerializeFn(serializer.UnescapeString(str));
887+
var parseFn = JsonReader.Instance.GetParseFn<T>();
888+
return parseFn(str);
852889
}
853890
}
854891

tests/ServiceStack.Text.Tests/JsonTests/BasicJsonTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,5 +537,35 @@ public void Explicit_DataMember_attribute_also_applies_to_public_fields()
537537
Assert.That(person.ToJsv().FromJsv<ModelWithDataMemberField>().Id, Is.EqualTo(1));
538538
Assert.That(person.ToJson().FromJson<ModelWithDataMemberField>().Id, Is.EqualTo(1));
539539
}
540+
541+
[Test]
542+
public void Can_include_null_values_for_adhoc_types()
543+
{
544+
Assert.That(new Foo().ToJson(), Is.EqualTo("{}"));
545+
546+
JsConfig<Foo>.RawSerializeFn = obj =>
547+
{
548+
using (JsConfig.With(includeNullValues: true))
549+
return obj.ToJson();
550+
};
551+
552+
Assert.That(new Foo().ToJson(), Is.EqualTo("{\"Bar\":null}"));
553+
554+
JsConfig.Reset();
555+
}
556+
557+
[Test]
558+
public void Can_run_FromJson_within_RawDeserializeFn()
559+
{
560+
JsConfig<Foo>.RawDeserializeFn = json =>
561+
{
562+
using (JsConfig.With(includeNullValues: true))
563+
return json.FromJson<Foo>();
564+
};
565+
566+
var obj = "{\"Bar\":\"Bar\"}".FromJson<Foo>();
567+
568+
Assert.That(obj.Bar, Is.EqualTo("Bar"));
569+
}
540570
}
541571
}

0 commit comments

Comments
 (0)