Skip to content

Commit a24ec9e

Browse files
committed
Add support for ValueTuple, Type and plain Object
Improve PropertyCSScriptSerialize constructor exception message
1 parent 5e5409d commit a24ec9e

14 files changed

+240
-106
lines changed

CSharpScriptSerializer.sln.DotSettings

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
1010
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
1111
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
12+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/CONSTRUCTOR_OR_DESTRUCTOR_BODY/@EntryValue">ExpressionBody</s:String>
13+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/LOCAL_FUNCTION_BODY/@EntryValue">ExpressionBody</s:String>
14+
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/METHOD_OR_OPERATOR_BODY/@EntryValue">ExpressionBody</s:String>
1215
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
16+
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
1317
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue"></s:String>
1418
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=DECLSPEC_005FPROPERTY/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
1519
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=ENUM/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>
@@ -49,6 +53,7 @@
4953
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
5054
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM_005FMEMBER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
5155
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FINTERFACE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /&gt;</s:String>
56+
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMIXED_005FENUM/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
5257
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
5358
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FEXPORTED/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
5459
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FLOCAL/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
@@ -64,6 +69,7 @@
6469
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
6570
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
6671
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FMETHOD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
72+
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
6773
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FPARAMETER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
6874
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
6975
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FHTML_005FCONTROL/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
@@ -72,6 +78,7 @@
7278
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
7379
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
7480
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
81+
<s:String x:Key="/Default/Environment/Hierarchy/PsiConfigurationSettingsKey/CustomLocation/@EntryValue">C:\Users\Andriy\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v09_9df6e669\SolutionCaches</s:String>
7582
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
7683
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
7784
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

src/CSharpScriptSerializer/CSScriptSerializer.cs

Lines changed: 88 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,28 @@ namespace CSharpScriptSerialization
1717
{
1818
public abstract class CSScriptSerializer : ICSScriptSerializer
1919
{
20-
protected CSScriptSerializer(Type type)
21-
{
22-
Type = type;
23-
}
24-
25-
public Type Type { get; }
26-
27-
public abstract ExpressionSyntax GetCreation(object obj);
28-
2920
public static readonly List<ICSScriptSerializerFactory> SerializerFactories =
3021
new List<ICSScriptSerializerFactory>();
3122

3223
public static readonly ConcurrentDictionary<Type, ICSScriptSerializer> Serializers =
3324
new ConcurrentDictionary<Type, ICSScriptSerializer>();
3425

35-
public static T Deserialize<T>(string script)
36-
=> DeserializeAsync<T>(script).GetAwaiter().GetResult();
26+
private static readonly ConcurrentDictionary<Type, object> TypeDefaults =
27+
new ConcurrentDictionary<Type, object>();
28+
29+
protected CSScriptSerializer(Type type) => Type = type;
30+
31+
public Type Type { get; }
32+
33+
public abstract ExpressionSyntax GetCreation(object obj);
34+
35+
public static T Deserialize<T>(string script) => DeserializeAsync<T>(script).GetAwaiter().GetResult();
3736

3837
public static Task<T> DeserializeAsync<T>(string script)
3938
=> DeserializeAsync<T>(script, Enumerable.Empty<Assembly>(), Enumerable.Empty<string>());
4039

41-
public static T Deserialize<T>(string script, IEnumerable<Assembly> referencedAssemblies, IEnumerable<string> imports)
40+
public static T Deserialize<T>(string script, IEnumerable<Assembly> referencedAssemblies,
41+
IEnumerable<string> imports)
4242
=> DeserializeAsync<T>(script, referencedAssemblies, imports).GetAwaiter().GetResult();
4343

4444
public static Task<T> DeserializeAsync<T>(
@@ -73,30 +73,23 @@ public static string Serialize(object obj)
7373
}
7474
}
7575

76-
public static CompilationUnitSyntax GetCompilationUnitExpression(object obj)
77-
=> CompilationUnit()
78-
.WithMembers(
79-
SingletonList<MemberDeclarationSyntax>(
80-
GlobalStatement(
81-
ExpressionStatement(GetCreationExpression(obj))
82-
.WithSemicolonToken(MissingToken(SyntaxKind.SemicolonToken)))));
76+
public static CompilationUnitSyntax GetCompilationUnitExpression(object obj) => CompilationUnit()
77+
.WithMembers(
78+
SingletonList<MemberDeclarationSyntax>(
79+
GlobalStatement(
80+
ExpressionStatement(GetCreationExpression(obj))
81+
.WithSemicolonToken(MissingToken(SyntaxKind.SemicolonToken)))));
8382

84-
public static ExpressionSyntax GetCreationExpression(object obj)
85-
{
86-
return GetSerializer(obj).GetCreation(obj);
87-
}
83+
public static ExpressionSyntax GetCreationExpression(object obj) => GetSerializer(obj).GetCreation(obj);
8884

8985
private static ICSScriptSerializer GetSerializer(object obj)
9086
{
91-
if (obj == null)
87+
switch (obj)
9288
{
93-
return NullCSScriptSerializer.Instance;
94-
}
95-
96-
var serializable = obj as ICSScriptSerializable;
97-
if (serializable != null)
98-
{
99-
return serializable.GetSerializer();
89+
case null:
90+
return NullCSScriptSerializer.Instance;
91+
case ICSScriptSerializable serializable:
92+
return serializable.GetSerializer();
10093
}
10194

10295
var type = UnwrapNullableType(obj.GetType());
@@ -166,6 +159,16 @@ private static ICSScriptSerializer CreateSerializer(Type type)
166159
new Func<TimeSpan, object>[] {t => t.Ticks});
167160
}
168161

162+
if (typeof(Type).GetTypeInfo().IsAssignableFrom(type))
163+
{
164+
return new TypeCSScriptSerializer(type);
165+
}
166+
167+
if (type == typeof(object))
168+
{
169+
return new ConstructorCSScriptSerializer<object>();
170+
}
171+
169172
if (type.IsArray)
170173
{
171174
return new ArrayCSScriptSerializer(type);
@@ -174,6 +177,7 @@ private static ICSScriptSerializer CreateSerializer(Type type)
174177
if (type.IsConstructedGenericType)
175178
{
176179
var genericDefinition = type.GetGenericTypeDefinition();
180+
177181
if (genericDefinition == typeof(Tuple<>))
178182
{
179183
return CreateConstructorCSScriptSerializer(type, CreateTupleGetters(type, arity: 1));
@@ -206,6 +210,39 @@ private static ICSScriptSerializer CreateSerializer(Type type)
206210
{
207211
return CreateConstructorCSScriptSerializer(type, CreateTupleGetters(type, arity: 8));
208212
}
213+
214+
if (genericDefinition == typeof(ValueTuple<>))
215+
{
216+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 1));
217+
}
218+
if (genericDefinition == typeof(ValueTuple<,>))
219+
{
220+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 2));
221+
}
222+
if (genericDefinition == typeof(ValueTuple<,,>))
223+
{
224+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 3));
225+
}
226+
if (genericDefinition == typeof(ValueTuple<,,,>))
227+
{
228+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 4));
229+
}
230+
if (genericDefinition == typeof(ValueTuple<,,,,>))
231+
{
232+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 5));
233+
}
234+
if (genericDefinition == typeof(ValueTuple<,,,,,>))
235+
{
236+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 6));
237+
}
238+
if (genericDefinition == typeof(ValueTuple<,,,,,,>))
239+
{
240+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 7));
241+
}
242+
if (genericDefinition == typeof(ValueTuple<,,,,,,,>))
243+
{
244+
return new ValueTupleCSScriptSerializer(type, CreateValueTupleGetters(type, arity: 8));
245+
}
209246
}
210247

211248
foreach (var additionalSerializerFactory in SerializerFactories)
@@ -259,17 +296,15 @@ private static IEnumerable<object> ToEnumerable(IEnumerator enumerator)
259296

260297
private static CSScriptSerializer CreateConstructorCSScriptSerializer(
261298
Type type,
262-
IReadOnlyCollection<Func<object, object>> parameterGetters)
263-
=> (CSScriptSerializer)GetDeclaredConstructor(
299+
IReadOnlyCollection<Func<object, object>> parameterGetters) => (CSScriptSerializer)GetDeclaredConstructor(
264300
typeof(ConstructorCSScriptSerializer<>).MakeGenericType(type),
265301
new[] {typeof(IReadOnlyCollection<Func<object, object>>)})
266302
.Invoke(new object[] {parameterGetters});
267303

268304
private static CSScriptSerializer CreateCollectionCSScriptSerializer(
269305
Type type,
270306
IReadOnlyCollection<Func<object, object>> elementDecomposers,
271-
Func<object, IEnumerable<object>> getEnumerable)
272-
=> (CSScriptSerializer)GetDeclaredConstructor(
307+
Func<object, IEnumerable<object>> getEnumerable) => (CSScriptSerializer)GetDeclaredConstructor(
273308
typeof(CollectionCSScriptSerializer<>).MakeGenericType(type),
274309
new[] {typeof(IReadOnlyCollection<Func<object, object>>), typeof(Func<object, IEnumerable<object>>)})
275310
.Invoke(new object[] {elementDecomposers, getEnumerable});
@@ -285,6 +320,17 @@ private static Func<object, object>[] CreateTupleGetters(Type type, int arity)
285320
return getters.ToArray();
286321
}
287322

323+
private static Func<object, object>[] CreateValueTupleGetters(Type type, int arity)
324+
{
325+
var getters = new List<Func<object, object>>();
326+
for (var i = 1; i <= arity; i++)
327+
{
328+
var itemField = type.GetTypeInfo().GetField("Item" + i);
329+
getters.Add(o => itemField.GetValue(o));
330+
}
331+
return getters.ToArray();
332+
}
333+
288334
protected static bool IsConstructable(Type type)
289335
=> !type.GetTypeInfo().IsInterface
290336
&& !type.GetTypeInfo().IsAbstract
@@ -312,17 +358,12 @@ protected static Type TryGetElementType(Type type, Type interfaceOrBaseType)
312358
return types.Count == 1 ? types[index: 0].GetTypeInfo().GenericTypeArguments.FirstOrDefault() : null;
313359
}
314360

315-
private static readonly ConcurrentDictionary<Type, object> TypeDefaults =
316-
new ConcurrentDictionary<Type, object>();
317-
318361
protected static object GetDefault(Type type)
319362
=> type.GetTypeInfo().IsValueType ? TypeDefaults.GetOrAdd(type, Activator.CreateInstance) : null;
320363

321-
protected static bool IsDefault(object obj)
322-
=> IsDefault(obj, obj.GetType());
364+
protected static bool IsDefault(object obj) => IsDefault(obj, obj.GetType());
323365

324-
protected static bool IsDefault(object obj, Type type)
325-
=> obj == null || obj.Equals(GetDefault(type));
366+
protected static bool IsDefault(object obj, Type type) => obj == null || obj.Equals(GetDefault(type));
326367

327368
protected static IEnumerable<Type> GetGenericTypeImplementations(Type type, Type interfaceOrBaseType)
328369
{
@@ -334,7 +375,7 @@ protected static IEnumerable<Type> GetGenericTypeImplementations(Type type, Type
334375
: GetBaseTypes(type))
335376
.Union(new[] {type})
336377
.Where(t => t.GetTypeInfo().IsGenericType
337-
&& (t.GetGenericTypeDefinition() == interfaceOrBaseType));
378+
&& t.GetGenericTypeDefinition() == interfaceOrBaseType);
338379
}
339380

340381
return Enumerable.Empty<Type>();
@@ -441,7 +482,7 @@ protected static TypeSyntax GetTypeSyntax(Type type)
441482
GetArrayRanks(type)));
442483
}
443484

444-
return GetNameSyntax(type, null);
485+
return GetNameSyntax(type, genericArguments: null);
445486
}
446487

447488
private static NameSyntax GetNameSyntax(Type type, List<Type> genericArguments)
@@ -454,8 +495,8 @@ private static NameSyntax GetNameSyntax(Type type, List<Type> genericArguments)
454495
if (declaringType != null)
455496
{
456497
var genericParameters = declaringType.GetTypeInfo().GenericTypeParameters;
457-
declaringTypeGenericArguments = genericArguments.GetRange(0, genericParameters.Length);
458-
genericArguments.RemoveRange(0, genericParameters.Length);
498+
declaringTypeGenericArguments = genericArguments.GetRange(index: 0, count: genericParameters.Length);
499+
genericArguments.RemoveRange(index: 0, count: genericParameters.Length);
459500
}
460501

461502
var simpleName = genericArguments.Count > 0
@@ -491,8 +532,7 @@ private static IEnumerable<ArrayRankSpecifierSyntax> GetArrayRanks(Type type)
491532
.Concat(GetArrayRanks(type.GetElementType()));
492533

493534
protected static TSyntax AddNewLine<TSyntax>(TSyntax expression)
494-
where TSyntax : SyntaxNode
495-
=> expression.FullSpan.Length > 120
535+
where TSyntax : SyntaxNode => expression.FullSpan.Length > 120
496536
? expression.WithLeadingTrivia(CarriageReturnLineFeed)
497537
.WithTrailingTrivia(CarriageReturnLineFeed)
498538
: expression;

src/CSharpScriptSerializer/CSharpScriptSerializer.csproj

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
<PackageId>CSharpScriptSerializer</PackageId>
66
<AssemblyTitle>CSharpScriptSerializer</AssemblyTitle>
77
<Title>CSharpScriptSerializer</Title>
8-
<VersionPrefix>1.3.0</VersionPrefix>
8+
<VersionPrefix>1.4.0</VersionPrefix>
99
<TargetFrameworks>netstandard1.5;net46</TargetFrameworks>
1010
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.5' ">1.6.1</NetStandardImplicitPackageVersion>
11-
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
12-
$(PackageTargetFallback);portable-net45+win8
13-
</PackageTargetFallback>
1411
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
1512
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
1613
<Description>Serialize to C# scripts</Description>
1714
<Authors>Andriy Svyryd</Authors>
1815
<PackageTags>Roslyn;CSharp;C#;CSX;Script;Serialization</PackageTags>
19-
<PackageReleaseNotes>Version 1.3.0
16+
<PackageReleaseNotes>
17+
<![CDATA[
18+
Version 1.4.0
19+
* Add support for ValueTuple, Type and plain Object
20+
Version 1.3.0
2021
* Update to Roslyn V2.3.1
2122
Version 1.2.0
2223
* Update to Roslyn V2
@@ -27,20 +28,16 @@ Version 1.1.1
2728
* Remove redundant flags enum values
2829
Version 1.1.0
2930
* Enable customizing the property serialization condition in PropertyCSScriptSerializer
30-
Version 1.0.0
31-
* Enable customizing the default values in PropertyCSScriptSerializer
32-
* Change ICSScriptSerializable.GetSerializer() to return ICSScriptSerializer
33-
* Fix parameters not being used in DeserializeAsync
34-
35-
Version 1.0.0-alpha1
36-
* Initial release</PackageReleaseNotes>
31+
]]>
32+
</PackageReleaseNotes>
3733
<PackageProjectUrl>https://github.com/AndriySvyryd/CSharpScriptSerializer</PackageProjectUrl>
3834
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
3935
<RepositoryType>git</RepositoryType>
4036
<RepositoryUrl>https://github.com/AndriySvyryd/CSharpScriptSerializer.git</RepositoryUrl>
4137
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
4238
<IncludeSymbols>True</IncludeSymbols>
4339
<IncludeSource>True</IncludeSource>
40+
<RootNamespace>CSharpScriptSerialization</RootNamespace>
4441
</PropertyGroup>
4542

4643
<ItemGroup>

0 commit comments

Comments
 (0)