Skip to content

Commit f14e65a

Browse files
committed
add ability to use custom json serialization options
1 parent cbd093d commit f14e65a

File tree

5 files changed

+233
-14
lines changed

5 files changed

+233
-14
lines changed

src/TableStorage.Abstractions.TableEntityConverters/EntityConvert.cs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,36 @@ namespace TableStorage.Abstractions.TableEntityConverters
1010
{
1111
public static class EntityConvert
1212
{
13-
public static DynamicTableEntity ToTableEntity<T>(this T o, string partitionKey, string rowKey, params Expression<Func<T, object>>[] ignoredProperties)
13+
private static JsonSerializerSettings _defaultJsonSerializerSettings = new JsonSerializerSettings();
14+
15+
public static JsonSerializerSettings DefaultJsonSerializerSettings {
16+
set => _defaultJsonSerializerSettings = value ?? new JsonSerializerSettings();
17+
}
18+
19+
public static DynamicTableEntity ToTableEntity<T>(this T o, string partitionKey, string rowKey,
20+
params Expression<Func<T, object>>[] ignoredProperties)
21+
{
22+
return ToTableEntity(o, partitionKey, rowKey, _defaultJsonSerializerSettings, ignoredProperties);
23+
}
24+
public static DynamicTableEntity ToTableEntity<T>(this T o, string partitionKey, string rowKey, JsonSerializerSettings jsonSerializerSettings, params Expression<Func<T, object>>[] ignoredProperties)
1425
{
26+
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
1527
var type = typeof(T);
1628
var properties = GetProperties(type);
1729
RemoveIgnoredProperties(properties, ignoredProperties);
18-
return CreateTableEntity(o, properties, partitionKey, rowKey);
30+
return CreateTableEntity(o, properties, partitionKey, rowKey, jsonSerializerSettings);
1931
}
2032

2133
public static DynamicTableEntity ToTableEntity<T>(this T o, Expression<Func<T, object>> partitionProperty,
2234
Expression<Func<T, object>> rowProperty, params Expression<Func<T, object>>[] ignoredProperties)
2335
{
36+
return ToTableEntity(o, partitionProperty, rowProperty, _defaultJsonSerializerSettings, ignoredProperties);
37+
}
38+
39+
public static DynamicTableEntity ToTableEntity<T>(this T o, Expression<Func<T, object>> partitionProperty,
40+
Expression<Func<T, object>> rowProperty, JsonSerializerSettings jsonSerializerSettings, params Expression<Func<T, object>>[] ignoredProperties)
41+
{
42+
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
2443
var type = typeof(T);
2544
var properties = GetProperties(type);
2645
var partitionProp =
@@ -43,13 +62,22 @@ public static DynamicTableEntity ToTableEntity<T>(this T o, Expression<Func<T, o
4362
var partitionKey = partitionProp.GetValue(o).ToString();
4463
var rowKey = rowProp.GetValue(o).ToString();
4564

46-
return CreateTableEntity(o, properties, partitionKey, rowKey);
65+
return CreateTableEntity(o, properties, partitionKey, rowKey, jsonSerializerSettings);
4766
}
4867

4968
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
5069
Expression<Func<T, object>> partitionProperty,
5170
Expression<Func<T, object>> rowProperty) where T : new()
5271
{
72+
return FromTableEntity<T, TP, TR>(entity, partitionProperty, rowProperty, _defaultJsonSerializerSettings);
73+
}
74+
75+
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
76+
Expression<Func<T, object>> partitionProperty,
77+
Expression<Func<T, object>> rowProperty, JsonSerializerSettings jsonSerializerSettings) where T : new()
78+
{
79+
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
80+
5381
var convertPartition = new Func<string, TP>(p => (TP)Convert.ChangeType(p, typeof(TP)));
5482
var convertRow = new Func<string, TR>(r => (TR)Convert.ChangeType(r, typeof(TR)));
5583

@@ -62,14 +90,25 @@ public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
6290
convertRow = r => (TR)(object)Guid.Parse(r);
6391
}
6492
return FromTableEntity(entity, partitionProperty, convertPartition,
65-
rowProperty, convertRow);
93+
rowProperty, convertRow, jsonSerializerSettings);
6694
}
6795

6896
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
6997
Expression<Func<T, object>> partitionProperty,
7098
Func<string, TP> convertPartitionKey, Expression<Func<T, object>> rowProperty,
7199
Func<string, TR> convertRowKey) where T : new()
72100
{
101+
return FromTableEntity(entity, partitionProperty, convertPartitionKey, rowProperty,
102+
convertRowKey, _defaultJsonSerializerSettings);
103+
}
104+
105+
public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
106+
Expression<Func<T, object>> partitionProperty,
107+
Func<string, TP> convertPartitionKey, Expression<Func<T, object>> rowProperty,
108+
Func<string, TR> convertRowKey, JsonSerializerSettings jsonSerializerSettings) where T : new()
109+
{
110+
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
111+
73112
var o = new T();
74113
var type = typeof(T);
75114
var properties = GetProperties(type);
@@ -99,13 +138,20 @@ public static T FromTableEntity<T, TP, TR>(this DynamicTableEntity entity,
99138
}
100139

101140
SetTimestamp(entity, o, properties);
102-
FillProperties(entity, o, properties);
141+
FillProperties(entity, o, properties, jsonSerializerSettings);
103142
return o;
104143
}
105144

106145
public static T FromTableEntity<T>(this DynamicTableEntity entity) where T : new()
107146
{
108-
return entity.FromTableEntity<T, object, object>(null, null, null, null);
147+
return FromTableEntity<T>(entity, _defaultJsonSerializerSettings);
148+
}
149+
150+
public static T FromTableEntity<T>(this DynamicTableEntity entity, JsonSerializerSettings jsonSerializerSettings) where T : new()
151+
{
152+
_ = jsonSerializerSettings ?? throw new ArgumentNullException(nameof(jsonSerializerSettings));
153+
154+
return entity.FromTableEntity<T, object, object>(null, null, null, null, jsonSerializerSettings);
109155
}
110156

111157
internal static string GetPropertyNameFromExpression<T>(Expression<Func<T, object>> exp)
@@ -151,7 +197,7 @@ internal static string GetPropertyNameFromExpression<T>(Expression<Func<T, objec
151197
}
152198
}
153199

154-
private static void FillProperties<T>(DynamicTableEntity entity, T o, List<PropertyInfo> properties) where T : new()
200+
private static void FillProperties<T>(DynamicTableEntity entity, T o, List<PropertyInfo> properties, JsonSerializerSettings jsonSerializerSettings) where T : new()
155201
{
156202
foreach (var propertyInfo in properties)
157203
{
@@ -191,15 +237,15 @@ internal static string GetPropertyNameFromExpression<T>(Expression<Func<T, objec
191237
var val = entity.Properties[$"{propertyInfo.Name}Json"].StringValue;
192238
if (val != null)
193239
{
194-
var propVal = JsonConvert.DeserializeObject(val, propertyInfo.PropertyType);
240+
var propVal = JsonConvert.DeserializeObject(val, propertyInfo.PropertyType, jsonSerializerSettings ?? _defaultJsonSerializerSettings);
195241
propertyInfo.SetValue(o, propVal);
196242
}
197243
}
198244
}
199245
}
200246

201247
private static DynamicTableEntity CreateTableEntity(object o, List<PropertyInfo> properties,
202-
string partitionKey, string rowKey)
248+
string partitionKey, string rowKey, JsonSerializerSettings jsonSerializerSettings)
203249
{
204250
var entity = new DynamicTableEntity(partitionKey, rowKey);
205251
foreach (var propertyInfo in properties)
@@ -247,7 +293,7 @@ private static DynamicTableEntity CreateTableEntity(object o, List<PropertyInfo>
247293
break;
248294
default:
249295
name += "Json";
250-
entityProperty = new EntityProperty(JsonConvert.SerializeObject(val));
296+
entityProperty = new EntityProperty(JsonConvert.SerializeObject(val, jsonSerializerSettings ?? _defaultJsonSerializerSettings));
251297
break;
252298
}
253299
entity.Properties[name] = entityProperty;

src/TableStorage.Abstractions.TableEntityConverters/TableStorage.Abstractions.TableEntityConverters.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
<Authors>Giovanni Galbo</Authors>
77
<Company>Giovanni Galbo</Company>
88
<Description>Easily convert POCOs (Plain Old CLR Objects) to Azure Table Storage TableEntities and vice versa.</Description>
9-
<Copyright>Giovanni Galbo © 2020</Copyright>
9+
<Copyright>Giovanni Galbo © 2021</Copyright>
1010
<PackageLicenseUrl></PackageLicenseUrl>
1111
<PackageProjectUrl>https://github.com/giometrix/TableStorage.Abstractions.TableEntityConverters</PackageProjectUrl>
1212
<RepositoryUrl>https://github.com/giometrix/TableStorage.Abstractions.TableEntityConverters</RepositoryUrl>
1313
<PackageTags>table-storage azure-table-storage poco table-entities</PackageTags>
14-
<PackageReleaseNotes>Update nuget packages</PackageReleaseNotes>
14+
<PackageReleaseNotes>Allow for custom json serialization</PackageReleaseNotes>
1515
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1616
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
1717
<PackageLicenseExpression>MIT</PackageLicenseExpression>
@@ -21,6 +21,8 @@
2121
<IncludeSymbols>true</IncludeSymbols>
2222
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
2323
<AssemblyVersion>1.3.2.0</AssemblyVersion>
24+
<Nullable>disable</Nullable>
25+
<PackageVersion>1.4.0.0</PackageVersion>
2426
</PropertyGroup>
2527

2628
<ItemGroup>

src/TableStorage.Abstractions.UnitTests/EntityConvertTests.cs

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
using System;
2+
using System.Collections.Generic;
3+
using Newtonsoft.Json;
24
using TableStorage.Abstractions.TableEntityConverters;
35
using Xunit;
46

57
namespace TableStorage.Abstractions.UnitTests
68
{
79
public class EntityConvertTests
810
{
11+
public EntityConvertTests()
12+
{
13+
EntityConvert.DefaultJsonSerializerSettings = null;
14+
}
15+
916
public class GuidKeyTest
1017
{
1118
public Guid A { get; set; }
@@ -284,6 +291,118 @@ public void convert_to_entity_table()
284291
var tableEntity = emp.ToTableEntity(e => e.Company, e => e.Id);
285292

286293
Assert.True(tableEntity.Properties.ContainsKey("DepartmentJson"));
294+
var dept = tableEntity.Properties["DepartmentJson"].StringValue.ToLowerInvariant();
295+
Assert.Contains("optionalid", dept);
296+
}
297+
298+
[Fact]
299+
public void convert_to_entity_table_custom_json_settings_as_a_global_setting()
300+
{
301+
EntityConvert.DefaultJsonSerializerSettings = new JsonSerializerSettings {
302+
NullValueHandling = NullValueHandling.Ignore,
303+
};
304+
305+
var emp = new Employee
306+
{
307+
Company = "Microsoft",
308+
Name = "John Smith",
309+
Department = new Department
310+
{
311+
Name = "QA",
312+
Id = 1,
313+
OptionalId = null
314+
},
315+
Id = 42,
316+
ExternalId = Guid.Parse("e3bf64f4-0537-495c-b3bf-148259d7ed36"),
317+
HireDate = DateTimeOffset.Parse("Thursday, January 31, 2008 ")
318+
};
319+
var tableEntity = emp.ToTableEntity(e => e.Company, e => e.Id);
320+
321+
var dept = tableEntity.Properties["DepartmentJson"].StringValue.ToLowerInvariant();
322+
Assert.DoesNotContain("optionalid", dept);
323+
}
324+
325+
[Fact]
326+
public void convert_to_entity_table_custom_json_settings_as_a_local_setting()
327+
{
328+
var jsonSerializerSettings = new JsonSerializerSettings {
329+
NullValueHandling = NullValueHandling.Ignore
330+
};
331+
332+
var emp = new Employee
333+
{
334+
Company = "Microsoft",
335+
Name = "John Smith",
336+
Department = new Department
337+
{
338+
Name = "QA",
339+
Id = 1,
340+
OptionalId = null
341+
},
342+
Id = 42,
343+
ExternalId = Guid.Parse("e3bf64f4-0537-495c-b3bf-148259d7ed36"),
344+
HireDate = DateTimeOffset.Parse("Thursday, January 31, 2008 ")
345+
};
346+
var tableEntity = emp.ToTableEntity(e => e.Company, e => e.Id, jsonSerializerSettings);
347+
348+
var dept = tableEntity.Properties["DepartmentJson"].StringValue.ToLowerInvariant();
349+
Assert.DoesNotContain("optionalid", dept);
350+
}
351+
352+
[Fact]
353+
public void convert_from_entity_using_custom_json_settings_as_a_global_setting()
354+
{
355+
EntityConvert.DefaultJsonSerializerSettings = new JsonSerializerSettings {
356+
Converters = new List<JsonConverter>{new KeysJsonConverter(typeof(Department))}
357+
};
358+
359+
var emp = new Employee
360+
{
361+
Company = "Microsoft",
362+
Name = "John Smith",
363+
Department = new Department
364+
{
365+
Name = "QA",
366+
Id = 1,
367+
OptionalId = null
368+
},
369+
Id = 42,
370+
ExternalId = Guid.Parse("e3bf64f4-0537-495c-b3bf-148259d7ed36"),
371+
HireDate = DateTimeOffset.Parse("Thursday, January 31, 2008 ")
372+
};
373+
var tableEntity = emp.ToTableEntity(e => e.Company, e => e.Id);
374+
var dept = tableEntity.Properties["DepartmentJson"].StringValue;
375+
Assert.Contains("Keys", dept);
376+
var deserializedEmp = tableEntity.FromTableEntity<Employee>();
377+
Assert.Equal("QA", deserializedEmp.Department.Name);
378+
}
379+
380+
[Fact]
381+
public void convert_from_entity_using_custom_json_settings_as_a_local_setting()
382+
{
383+
var jsonSerializerSettings = new JsonSerializerSettings {
384+
Converters = new List<JsonConverter>{new KeysJsonConverter(typeof(Department))}
385+
};
386+
387+
var emp = new Employee
388+
{
389+
Company = "Microsoft",
390+
Name = "John Smith",
391+
Department = new Department
392+
{
393+
Name = "QA",
394+
Id = 1,
395+
OptionalId = null
396+
},
397+
Id = 42,
398+
ExternalId = Guid.Parse("e3bf64f4-0537-495c-b3bf-148259d7ed36"),
399+
HireDate = DateTimeOffset.Parse("Thursday, January 31, 2008 ")
400+
};
401+
var tableEntity = emp.ToTableEntity(e => e.Company, e => e.Id, jsonSerializerSettings);
402+
var dept = tableEntity.Properties["DepartmentJson"].StringValue;
403+
Assert.Contains("Keys", dept);
404+
var deserializedEmp = tableEntity.FromTableEntity<Employee>(jsonSerializerSettings);
405+
Assert.Equal("QA", deserializedEmp.Department.Name);
287406
}
288407

289408
[Fact]
@@ -323,7 +442,7 @@ public void convert_to_entity_table_ignore_complex_properties()
323442
ExternalId = Guid.Parse("e3bf64f4-0537-495c-b3bf-148259d7ed36"),
324443
HireDate = DateTimeOffset.Parse("Thursday, January 31, 2008 ")
325444
};
326-
var tableEntity = emp.ToTableEntity(e => e.Company, e => e.Id, e => e.Department);
445+
var tableEntity = emp.ToTableEntity(e => e.Company, e => e.Id,e => e.Department);
327446
Assert.Equal("Google", tableEntity.PartitionKey);
328447
Assert.True(tableEntity.Properties.ContainsKey("ExternalId"));
329448
Assert.True(tableEntity.Properties.ContainsKey("HireDate"));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Newtonsoft.Json;
5+
using Newtonsoft.Json.Linq;
6+
7+
namespace TableStorage.Abstractions.UnitTests
8+
{
9+
public class KeysJsonConverter : JsonConverter
10+
{
11+
private readonly Type[] _types;
12+
13+
public KeysJsonConverter(params Type[] types)
14+
{
15+
_types = types;
16+
}
17+
18+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
19+
{
20+
JToken t = JToken.FromObject(value);
21+
22+
if (t.Type != JTokenType.Object)
23+
{
24+
t.WriteTo(writer);
25+
}
26+
else
27+
{
28+
JObject o = (JObject)t;
29+
IList<string> propertyNames = o.Properties().Select(p => p.Name).ToList();
30+
31+
o.AddFirst(new JProperty("Keys", new JArray(propertyNames)));
32+
33+
o.WriteTo(writer);
34+
}
35+
}
36+
37+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
38+
{
39+
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
40+
}
41+
42+
public override bool CanRead
43+
{
44+
get { return false; }
45+
}
46+
47+
public override bool CanConvert(Type objectType)
48+
{
49+
return _types.Any(t => t == objectType);
50+
}
51+
}
52+
}

src/TableStorage.Abstractions.UnitTests/TableStorage.Abstractions.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp2.0</TargetFramework>
4+
<TargetFramework>net5.0</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>

0 commit comments

Comments
 (0)