Browse Source

Serialization improved.

pull/169/head
Sebastian Stehle 8 years ago
parent
commit
9aa22c2444
  1. 3
      src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs
  2. 3
      src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs
  3. 3
      src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs
  4. 9
      src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs
  5. 3
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs
  6. 15
      src/Squidex.Infrastructure/CQRS/Events/Envelope{T}.cs
  7. 2
      src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs
  8. 3
      src/Squidex.Infrastructure/Json/ClaimsPrincipalConverter.cs
  9. 4
      src/Squidex.Infrastructure/Json/JsonClassConverter.cs
  10. 3
      src/Squidex.Infrastructure/Json/LanguageConverter.cs
  11. 2
      src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs
  12. 2
      src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs
  13. 2
      src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs
  14. 14
      src/Squidex.Infrastructure/Json/Orleans/JsonExternalSerializer.cs
  15. 10
      src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs
  16. 3
      src/Squidex.Infrastructure/Json/RefTokenConverter.cs
  17. 2
      src/Squidex.Infrastructure/Reflection/SimpleMapper.cs
  18. 4
      src/Squidex/Config/Domain/SerializationServices.cs
  19. 32
      tests/Squidex.Infrastructure.Tests/CQRS/Events/EnvelopeTests.cs
  20. 2
      tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs
  21. 51
      tests/Squidex.Infrastructure.Tests/CQRS/Events/RetrySubscriptionTests.cs
  22. 20
      tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs
  23. 2
      tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs
  24. 21
      tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs

3
src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@ -26,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
serializer.Serialize(writer, json);
}
protected override AppClients ReadValue(JsonReader reader, JsonSerializer serializer)
protected override AppClients ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<string, JsonAppClient>>(reader);

3
src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@ -26,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
serializer.Serialize(writer, json);
}
protected override AppContributors ReadValue(JsonReader reader, JsonSerializer serializer)
protected override AppContributors ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<string, AppContributorPermission>>(reader);

3
src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@ -26,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
serializer.Serialize(writer, json);
}
protected override LanguagesConfig ReadValue(JsonReader reader, JsonSerializer serializer)
protected override LanguagesConfig ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<string, JsonLanguageConfig>>(reader);

9
src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@ -13,14 +14,14 @@ namespace Squidex.Domain.Apps.Core.Rules.Json
{
public sealed class RuleConverter : JsonClassConverter<Rule>
{
protected override Rule ReadValue(JsonReader reader, JsonSerializer serializer)
protected override void WriteValue(JsonWriter writer, Rule value, JsonSerializer serializer)
{
return serializer.Deserialize<JsonRule>(reader).ToRule();
serializer.Serialize(writer, new JsonRule(value));
}
protected override void WriteValue(JsonWriter writer, Rule value, JsonSerializer serializer)
protected override Rule ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
serializer.Serialize(writer, new JsonRule(value));
return serializer.Deserialize<JsonRule>(reader).ToRule();
}
}
}

3
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
@ -28,7 +29,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
serializer.Serialize(writer, new JsonSchemaModel(value));
}
protected override Schema ReadValue(JsonReader reader, JsonSerializer serializer)
protected override Schema ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
return serializer.Deserialize<JsonSchemaModel>(reader).ToSchema(fieldRegistry);
}

15
src/Squidex.Infrastructure/CQRS/Events/Envelope{T}.cs

@ -23,23 +23,12 @@ namespace Squidex.Infrastructure.CQRS.Events
get { return payload; }
}
public Envelope(T payload)
: this(payload, new EnvelopeHeaders())
{
}
public Envelope(T payload, PropertiesBag bag)
: this(payload, new EnvelopeHeaders(bag))
{
}
public Envelope(T payload, EnvelopeHeaders headers)
public Envelope(T payload, EnvelopeHeaders headers = null)
{
Guard.NotNull(payload, nameof(payload));
Guard.NotNull(headers, nameof(headers));
this.payload = payload;
this.headers = headers;
this.headers = headers ?? new EnvelopeHeaders();
}
public Envelope<TOther> To<TOther>() where TOther : class

2
src/Squidex.Infrastructure/CQRS/Events/EventDataFormatter.cs

@ -27,7 +27,7 @@ namespace Squidex.Infrastructure.CQRS.Events
public virtual Envelope<IEvent> Parse(EventData eventData, bool migrate = true)
{
var headers = ReadJson<PropertiesBag>(eventData.Metadata);
var headers = ReadJson<EnvelopeHeaders>(eventData.Metadata);
var eventType = typeNameRegistry.GetType(eventData.Type);
var eventPayload = ReadJson<IEvent>(eventData.Payload, eventType);

3
src/Squidex.Infrastructure/Json/ClaimsPrincipalConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using System.Security.Claims;
using Newtonsoft.Json;
@ -48,7 +49,7 @@ namespace Squidex.Infrastructure.Json
serializer.Serialize(writer, jsonIdentities);
}
protected override ClaimsPrincipal ReadValue(JsonReader reader, JsonSerializer serializer)
protected override ClaimsPrincipal ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var jsonIdentities = serializer.Deserialize<JsonIdentity[]>(reader);

4
src/Squidex.Infrastructure/Json/JsonClassConverter.cs

@ -20,7 +20,7 @@ namespace Squidex.Infrastructure.Json
return null;
}
return ReadValue(reader, serializer);
return ReadValue(reader, objectType, serializer);
}
public sealed override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
@ -41,6 +41,6 @@ namespace Squidex.Infrastructure.Json
protected abstract void WriteValue(JsonWriter writer, T value, JsonSerializer serializer);
protected abstract T ReadValue(JsonReader reader, JsonSerializer serializer);
protected abstract T ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer);
}
}

3
src/Squidex.Infrastructure/Json/LanguageConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json
@ -17,7 +18,7 @@ namespace Squidex.Infrastructure.Json
writer.WriteValue(value.Iso2Code);
}
protected override Language ReadValue(JsonReader reader, JsonSerializer serializer)
protected override Language ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.String)
{

2
src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs

@ -19,7 +19,7 @@ namespace Squidex.Infrastructure.Json
writer.WriteValue($"{value.Id},{value.Name}");
}
protected override NamedId<Guid> ReadValue(JsonReader reader, JsonSerializer serializer)
protected override NamedId<Guid> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.String)
{

2
src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs

@ -19,7 +19,7 @@ namespace Squidex.Infrastructure.Json
writer.WriteValue($"{value.Id},{value.Name}");
}
protected override NamedId<long> ReadValue(JsonReader reader, JsonSerializer serializer)
protected override NamedId<long> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.String)
{

2
src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs

@ -19,7 +19,7 @@ namespace Squidex.Infrastructure.Json
writer.WriteValue($"{value.Id},{value.Name}");
}
protected override NamedId<string> ReadValue(JsonReader reader, JsonSerializer serializer)
protected override NamedId<string> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.String)
{

14
src/Squidex.Infrastructure/Json/Orleans/JsonExternalSerializer.cs

@ -18,13 +18,13 @@ namespace Squidex.Infrastructure.Json.Orleans
{
public class JsonExternalSerializer : IExternalSerializer
{
private readonly JsonSerializer jsonSerializer;
private readonly JsonSerializer serializer;
public JsonExternalSerializer(JsonSerializer jsonSerializer)
public JsonExternalSerializer(JsonSerializer serializer)
{
Guard.NotNull(jsonSerializer, nameof(jsonSerializer));
Guard.NotNull(serializer, nameof(serializer));
this.jsonSerializer = jsonSerializer;
this.serializer = serializer;
}
public void Initialize(Logger logger)
@ -38,7 +38,7 @@ namespace Squidex.Infrastructure.Json.Orleans
public object DeepCopy(object source, ICopyContext context)
{
return source != null ? JObject.FromObject(source, jsonSerializer).ToObject(source.GetType(), jsonSerializer) : null;
return source != null ? JObject.FromObject(source, serializer).ToObject(source.GetType(), serializer) : null;
}
public object Deserialize(Type expectedType, IDeserializationContext context)
@ -50,7 +50,7 @@ namespace Squidex.Infrastructure.Json.Orleans
using (var reader = new JsonTextReader(new StreamReader(stream)))
{
return jsonSerializer.Deserialize(reader, expectedType);
return serializer.Deserialize(reader, expectedType);
}
}
@ -60,7 +60,7 @@ namespace Squidex.Infrastructure.Json.Orleans
using (var writer = new JsonTextWriter(new StreamWriter(stream)))
{
jsonSerializer.Serialize(writer, item);
serializer.Serialize(writer, item);
writer.Flush();
}

10
src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs

@ -13,9 +13,9 @@ using NodaTime.Extensions;
namespace Squidex.Infrastructure.Json
{
public sealed class PropertiesBagConverter : JsonClassConverter<PropertiesBag>
public sealed class PropertiesBagConverter<T> : JsonClassConverter<T> where T : PropertiesBag, new()
{
protected override void WriteValue(JsonWriter writer, PropertiesBag value, JsonSerializer serializer)
protected override void WriteValue(JsonWriter writer, T value, JsonSerializer serializer)
{
writer.WriteStartObject();
@ -36,14 +36,14 @@ namespace Squidex.Infrastructure.Json
writer.WriteEndObject();
}
protected override PropertiesBag ReadValue(JsonReader reader, JsonSerializer serializer)
protected override T ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
throw new JsonException($"Expected Object, but got {reader.TokenType}.");
}
var properties = new PropertiesBag();
var properties = new T();
while (reader.Read())
{
@ -73,7 +73,7 @@ namespace Squidex.Infrastructure.Json
public override bool CanConvert(Type objectType)
{
return typeof(PropertiesBag).IsAssignableFrom(objectType);
return objectType == typeof(T);
}
}
}

3
src/Squidex.Infrastructure/Json/RefTokenConverter.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json
@ -17,7 +18,7 @@ namespace Squidex.Infrastructure.Json
writer.WriteValue(value.ToString());
}
protected override RefToken ReadValue(JsonReader reader, JsonSerializer serializer)
protected override RefToken ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.String)
{

2
src/Squidex.Infrastructure/Reflection/SimpleMapper.cs

@ -121,7 +121,7 @@ namespace Squidex.Infrastructure.Reflection
new PropertyAccessor(sourceClassType, sourceProperty),
new PropertyAccessor(targetClassType, targetProperty)));
}
else if (targetType.Implements<IConvertible>() && sourceType.Implements<IConvertible>())
else if (targetType.Implements<IConvertible>())
{
Mappers.Add(new ConversionPropertyMapper(
new PropertyAccessor(sourceClassType, sourceProperty),

4
src/Squidex/Config/Domain/SerializationServices.cs

@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Json;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb;
@ -48,7 +49,8 @@ namespace Squidex.Config.Domain
new NamedGuidIdConverter(),
new NamedLongIdConverter(),
new NamedStringIdConverter(),
new PropertiesBagConverter(),
new PropertiesBagConverter<EnvelopeHeaders>(),
new PropertiesBagConverter<PropertiesBag>(),
new RefTokenConverter(),
new RuleConverter(),
new SchemaConverter(FieldRegistry),

32
tests/Squidex.Infrastructure.Tests/CQRS/Events/EnvelopeTests.cs

@ -0,0 +1,32 @@
// ==========================================================================
// EnvelopeTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.TestHelpers;
using Xunit;
namespace Squidex.Infrastructure.CQRS.Events
{
public class EnvelopeTests
{
public class MyEvent : IEvent
{
public int Value { get; set; }
}
[Fact]
public void Should_serialize_and_deserialize()
{
var value = new Envelope<IEvent>(new MyEvent { Value = 1 });
var deserialized = value.SerializeAndDeserializeAndReturn(new PropertiesBagConverter<EnvelopeHeaders>());
Assert.Equal(1, value.To<MyEvent>().Payload.Value);
}
}
}

2
tests/Squidex.Infrastructure.Tests/CQRS/Events/EventDataFormatterTests.cs

@ -38,7 +38,7 @@ namespace Squidex.Infrastructure.CQRS.Events
public EventDataFormatterTests()
{
serializerSettings.Converters.Add(new PropertiesBagConverter());
serializerSettings.Converters.Add(new PropertiesBagConverter<EnvelopeHeaders>());
typeNameRegistry.Map(typeof(MyEvent), "Event");
typeNameRegistry.Map(typeof(MyOldEvent), "OldEvent");

51
tests/Squidex.Infrastructure.Tests/CQRS/Events/RetrySubscriptionTests.cs

@ -63,17 +63,29 @@ namespace Squidex.Infrastructure.CQRS.Events
var ex = new InvalidOperationException();
await OnErrorAsync(eventSubscription, ex);
await OnErrorAsync(eventSubscription, ex);
await OnErrorAsync(eventSubscription, ex);
await OnErrorAsync(eventSubscription, ex);
await OnErrorAsync(eventSubscription, ex);
await OnErrorAsync(eventSubscription, ex);
await OnErrorAsync(null, ex);
await OnErrorAsync(null, ex);
await OnErrorAsync(null, ex);
await OnErrorAsync(null, ex);
await OnErrorAsync(null, ex);
await sut.StopAsync();
A.CallTo(() => eventSubscriber.OnErrorAsync(sut, ex))
.MustHaveHappened();
}
[Fact]
public async Task Should_not_forward_error_when_exception_is_from_another_subscription()
{
var ex = new InvalidOperationException();
await OnErrorAsync(A.Fake<IEventSubscription>(), ex);
await sut.StopAsync();
A.CallTo(() => eventSubscriber.OnErrorAsync(A<IEventSubscription>.Ignored, A<Exception>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_forward_event_from_inner_subscription()
{
@ -87,26 +99,34 @@ namespace Squidex.Infrastructure.CQRS.Events
}
[Fact]
public async Task Should_not_forward_error_when_exception_is_from_another_subscription()
public async Task Should_not_forward_event_when_message_is_from_another_subscription()
{
var ex = new InvalidOperationException();
var ev = new StoredEvent("1", 2, new EventData());
await OnErrorAsync(A.Fake<IEventSubscription>(), ex);
await OnEventAsync(A.Fake<IEventSubscription>(), ev);
await sut.StopAsync();
A.CallTo(() => eventSubscriber.OnErrorAsync(A<IEventSubscription>.Ignored, A<Exception>.Ignored))
A.CallTo(() => eventSubscriber.OnEventAsync(A<IEventSubscription>.Ignored, A<StoredEvent>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_not_forward_event_when_message_is_from_another_subscription()
public async Task Should_forward_closed_from_inner_subscription()
{
var ev = new StoredEvent("1", 2, new EventData());
await OnClosedAsync(eventSubscription);
await sut.StopAsync();
await OnEventAsync(A.Fake<IEventSubscription>(), ev);
A.CallTo(() => eventSubscriber.OnClosedAsync(sut))
.MustHaveHappened();
}
[Fact]
public async Task Should_not_forward_closed_when_message_is_from_another_subscription()
{
await OnClosedAsync(A.Fake<IEventSubscription>());
await sut.StopAsync();
A.CallTo(() => eventSubscriber.OnEventAsync(A<IEventSubscription>.Ignored, A<StoredEvent>.Ignored))
A.CallTo(() => eventSubscriber.OnClosedAsync(sut))
.MustNotHaveHappened();
}
@ -119,5 +139,10 @@ namespace Squidex.Infrastructure.CQRS.Events
{
return sutSubscriber.OnEventAsync(subscriber, ev);
}
private Task OnClosedAsync(IEventSubscription subscriber)
{
return sutSubscriber.OnClosedAsync(subscriber);
}
}
}

20
tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs

@ -10,9 +10,9 @@ using System;
using System.Globalization;
using System.Linq;
using Microsoft.CSharp.RuntimeBinder;
using Newtonsoft.Json;
using NodaTime;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.TestHelpers;
using Xunit;
namespace Squidex.Infrastructure
@ -31,12 +31,7 @@ namespace Squidex.Infrastructure
[Fact]
public void Should_serialize_and_deserialize_empty_bag()
{
var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new PropertiesBagConverter());
var content = JsonConvert.SerializeObject(bag, serializerSettings);
var output = JsonConvert.DeserializeObject<PropertiesBag>(content, serializerSettings);
var output = bag.SerializeAndDeserializeAndReturn(new PropertiesBagConverter<PropertiesBag>());
Assert.Equal(bag.Count, output.Count);
}
@ -52,19 +47,14 @@ namespace Squidex.Infrastructure
bag.Set("Key4", true);
bag.Set("Key5", Guid.NewGuid());
var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new PropertiesBagConverter());
var content = JsonConvert.SerializeObject(bag, serializerSettings);
var response = JsonConvert.DeserializeObject<PropertiesBag>(content, serializerSettings);
var output = bag.SerializeAndDeserializeAndReturn(new PropertiesBagConverter<PropertiesBag>());
foreach (var kvp in response.Properties.Take(4))
foreach (var kvp in output.Properties.Take(4))
{
Assert.Equal(kvp.Value.RawValue, bag[kvp.Key].RawValue);
}
Assert.Equal(bag["Key5"].ToGuid(c), response["Key5"].ToGuid(c));
Assert.Equal(bag["Key5"].ToGuid(c), output["Key5"].ToGuid(c));
}
[Fact]

2
tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs

@ -70,7 +70,7 @@ namespace Squidex.Infrastructure.Reflection
}
[Fact]
public void Should_to_type()
public void Should_map_to_type()
{
var class1 = new MyClass1
{

21
tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs

@ -38,10 +38,7 @@ namespace Squidex.Infrastructure.TestHelpers
public static T SerializeAndDeserializeAndReturn<T>(this T value, JsonConverter converter)
{
var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(converter);
serializerSettings.NullValueHandling = NullValueHandling.Include;
var serializerSettings = CreateSettings<T>(converter);
var result = JsonConvert.SerializeObject(Tuple.Create(value), serializerSettings);
var output = JsonConvert.DeserializeObject<Tuple<T>>(result, serializerSettings);
@ -50,13 +47,25 @@ namespace Squidex.Infrastructure.TestHelpers
}
public static void DoesNotDeserialize<T>(string value, JsonConverter converter)
{
var serializerSettings = CreateSettings<T>(converter);
Assert.ThrowsAny<JsonException>(() => JsonConvert.DeserializeObject<Tuple<T>>($"{{ \"Item1\": \"{value}\" }}", serializerSettings));
}
private static JsonSerializerSettings CreateSettings<T>(JsonConverter converter)
{
var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(converter);
if (converter != null)
{
serializerSettings.Converters.Add(converter);
}
serializerSettings.NullValueHandling = NullValueHandling.Include;
serializerSettings.TypeNameHandling = TypeNameHandling.Auto;
Assert.ThrowsAny<JsonException>(() => JsonConvert.DeserializeObject<Tuple<T>>($"{{ \"Item1\": \"{value}\" }}", serializerSettings));
return serializerSettings;
}
}
}

Loading…
Cancel
Save