diff --git a/lib/tests/FSharp.Core.dll b/lib/tests/FSharp.Core.dll new file mode 100644 index 000000000..1b9afb668 Binary files /dev/null and b/lib/tests/FSharp.Core.dll differ diff --git a/src/ServiceStack.Text.sln b/src/ServiceStack.Text.sln index fb9dbbad6..18d55d173 100644 --- a/src/ServiceStack.Text.sln +++ b/src/ServiceStack.Text.sln @@ -11,11 +11,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack.Text", "Servic EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack.Text.Tests", "..\tests\ServiceStack.Text.Tests\ServiceStack.Text.Tests.csproj", "{9770BD40-AA3B-4785-B5E0-F4C470F9F14E}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ServiceStack.Text.FSharp.Tests", "..\tests\ServiceStack.Text.FSharp.Tests\ServiceStack.Text.FSharp.Tests.fsproj", "{1F03048A-D5B2-4950-9FAD-632A5DE78C94}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU MonoTouch|Any CPU = MonoTouch|Any CPU + Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {579B3FDB-CDAD-44E1-8417-885C38E49A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -29,8 +31,15 @@ Global {9770BD40-AA3B-4785-B5E0-F4C470F9F14E}.MonoTouch|Any CPU.ActiveCfg = MonoTouch|Any CPU {9770BD40-AA3B-4785-B5E0-F4C470F9F14E}.Release|Any CPU.ActiveCfg = Release|Any CPU {9770BD40-AA3B-4785-B5E0-F4C470F9F14E}.Release|Any CPU.Build.0 = Release|Any CPU + {1F03048A-D5B2-4950-9FAD-632A5DE78C94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F03048A-D5B2-4950-9FAD-632A5DE78C94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F03048A-D5B2-4950-9FAD-632A5DE78C94}.MonoTouch|Any CPU.ActiveCfg = Release|Any CPU + {1F03048A-D5B2-4950-9FAD-632A5DE78C94}.MonoTouch|Any CPU.Build.0 = Release|Any CPU + {1F03048A-D5B2-4950-9FAD-632A5DE78C94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F03048A-D5B2-4950-9FAD-632A5DE78C94}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection - GlobalSection(NestedProjects) = preSolution + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = ServiceStack.Text\ServiceStack.Text.csproj @@ -42,7 +51,4 @@ Global $2.Text = $2.IncludeInNewFiles = True EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection EndGlobal diff --git a/src/ServiceStack.Text.suo b/src/ServiceStack.Text.suo index 28605b110..60b077e17 100644 Binary files a/src/ServiceStack.Text.suo and b/src/ServiceStack.Text.suo differ diff --git a/src/ServiceStack.Text/Common/DeserializeType.cs b/src/ServiceStack.Text/Common/DeserializeType.cs index 32867ca49..f7476d899 100644 --- a/src/ServiceStack.Text/Common/DeserializeType.cs +++ b/src/ServiceStack.Text/Common/DeserializeType.cs @@ -50,7 +50,7 @@ public static ParseStringDelegate GetParseMethod(Type type) } private static object StringToType(Type type, string strType, - EmptyCtorDelegate ctorFn, + EmptyCtorDelegate ctorFn, IDictionary setterMap, IDictionary parseStringFnMap) { @@ -78,15 +78,15 @@ private static object StringToType(Type type, string strType, var propertyValueString = Serializer.EatValue(strType, ref index); - if (!parseStringFnMap.TryGetValue(propertyName, out parseStringFn)) - { - // try changing case of the first character - var p2 = propertyName.ToggleFirstChar(); - if (p2 != null && parseStringFnMap.TryGetValue(p2, out parseStringFn)) - { - propertyName = p2; - } - } + if (!parseStringFnMap.TryGetValue(propertyName, out parseStringFn)) + { + // try changing case of the first character + var p2 = propertyName.ToggleFirstChar(); + if (p2 != null && parseStringFnMap.TryGetValue(p2, out parseStringFn)) + { + propertyName = p2; + } + } if (parseStringFn != null) { @@ -106,10 +106,34 @@ private static object StringToType(Type type, string strType, return instance; } + private static SetPropertyDelegate FSharpRecordSetPropertyPolicy(PropertyInfo property) + { + string propertyName = property.Name; + string fieldName = propertyName.EndsWith("@") ? propertyName : propertyName + "@"; + FieldInfo fi = property.DeclaringType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + if (fi == null) return null; + return GetSetFieldMethod(property.DeclaringType, fi); + } + + public static SetPropertyDelegate GetSetFieldMethod(Type type, FieldInfo fieldInfo) + { + var oInstanceParam = Expression.Parameter(typeof(object), "oInstanceParam"); + var oValueParam = Expression.Parameter(typeof(object), "oValueParam"); + var instanceParam = Expression.Convert(oInstanceParam, type); + var useType = fieldInfo.FieldType; + var valueParam = Expression.Convert(oValueParam, useType); + return Expression.Lambda( + Expression.Assign(Expression.Field(instanceParam, fieldInfo), valueParam), + oInstanceParam, + oValueParam) + .Compile(); + } + public static SetPropertyDelegate GetSetPropertyMethod(Type type, PropertyInfo propertyInfo) { var setMethodInfo = propertyInfo.GetSetMethod(true); - if (setMethodInfo == null) return null; + // if there's no setter, let's see if we can try the F# policy of a private field suffixed by @. + if (setMethodInfo == null) return FSharpRecordSetPropertyPolicy(propertyInfo); #if SILVERLIGHT || MONOTOUCH return (instance, value) => setMethodInfo.Invoke(instance, new[] {value}); diff --git a/src/ServiceStack.Text/ServiceStack.Text.csproj b/src/ServiceStack.Text/ServiceStack.Text.csproj index 4d8f3aa5e..81e57117c 100644 --- a/src/ServiceStack.Text/ServiceStack.Text.csproj +++ b/src/ServiceStack.Text/ServiceStack.Text.csproj @@ -10,7 +10,7 @@ Properties ServiceStack.Text ServiceStack.Text - v3.5 + v4.0 512 @@ -31,6 +31,7 @@ false false true + true diff --git a/tests/ServiceStack.Text.FSharp.Tests/Records.fs b/tests/ServiceStack.Text.FSharp.Tests/Records.fs new file mode 100644 index 000000000..8eea2bd1f --- /dev/null +++ b/tests/ServiceStack.Text.FSharp.Tests/Records.fs @@ -0,0 +1,6 @@ +namespace ServiceStack.Text.FSharp.Tests + +type Coordinate = {X:double; Y:double} + +type Line = {Start:Coordinate; End:Coordinate} + diff --git a/tests/ServiceStack.Text.FSharp.Tests/ServiceStack.Text.FSharp.Tests.fsproj b/tests/ServiceStack.Text.FSharp.Tests/ServiceStack.Text.FSharp.Tests.fsproj new file mode 100644 index 000000000..e621a3797 --- /dev/null +++ b/tests/ServiceStack.Text.FSharp.Tests/ServiceStack.Text.FSharp.Tests.fsproj @@ -0,0 +1,54 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {1f03048a-d5b2-4950-9fad-632a5de78c94} + Library + ServiceStack.Text.FSharp.Tests + ServiceStack.Text.FSharp.Tests + v4.0 + ServiceStack.Text.FSharp.Tests + + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + + + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/ServiceStack.Text.Tests/JsonTests/BasicJsonTests.cs b/tests/ServiceStack.Text.Tests/JsonTests/BasicJsonTests.cs index 4236052cb..3b07d56da 100644 --- a/tests/ServiceStack.Text.Tests/JsonTests/BasicJsonTests.cs +++ b/tests/ServiceStack.Text.Tests/JsonTests/BasicJsonTests.cs @@ -70,6 +70,24 @@ public IntIntDictionary() public IDictionary Dictionary { get; set; } } + [Test] + public void Can_deserialize_simple_fsharp_record() + { + var s = JsonSerializer.DeserializeFromString("{\"X\":1.0,\"Y\":2.0}"); + Assert.AreEqual(1.0, s.X); + Assert.AreEqual(2.0, s.Y); + } + + [Test] + public void Can_deserialize_nested_fsharp_record() + { + var s = JsonSerializer.DeserializeFromString("{\"Start\":{\"X\":1.0,\"Y\":2.0},\"End\":{\"X\":3.0,\"Y\":4.0}}"); + Assert.AreEqual(1.0, s.Start.X); + Assert.AreEqual(2.0, s.Start.Y); + Assert.AreEqual(3.0, s.End.X); + Assert.AreEqual(4.0, s.End.Y); + } + [Test] public void Serialize_skips_null_values_by_default() { diff --git a/tests/ServiceStack.Text.Tests/ServiceStack.Text.Tests.csproj b/tests/ServiceStack.Text.Tests/ServiceStack.Text.Tests.csproj index 0d89d6aaf..5c062e677 100644 --- a/tests/ServiceStack.Text.Tests/ServiceStack.Text.Tests.csproj +++ b/tests/ServiceStack.Text.Tests/ServiceStack.Text.Tests.csproj @@ -10,7 +10,7 @@ Properties ServiceStack.Text.Tests ServiceStack.Text.Tests - v3.5 + v4.0 512 @@ -31,6 +31,7 @@ false false true + true @@ -61,6 +62,9 @@ AllRules.ruleset + + ..\..\lib\tests\FSharp.Core.dll + 3.5 @@ -206,6 +210,10 @@ {579B3FDB-CDAD-44E1-8417-885C38E49A0E} ServiceStack.Text + + {1F03048A-D5B2-4950-9FAD-632A5DE78C94} + ServiceStack.Text.FSharp.Tests +