Skip to content

Commit

Permalink
Merge pull request Kros-sk#14 from Kros-sk/JsonConverter
Browse files Browse the repository at this point in the history
Add JsonConverter for converting entity to/from JSON
  • Loading branch information
satano authored Nov 26, 2019
2 parents 8ee8642 + 925531a commit de6e67a
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ To contribute with new topics/information or make changes, see [contributing](ht
* [ASP.NET Core Extensions](#aspnet-core-extensions)
* [Database Migrations](#database-migrations)
* [Id Generators](#id-generators)
* [Converters](#converters)

### ASP.NET Core Extensions

Expand Down Expand Up @@ -155,3 +156,18 @@ public void ConfigureServices(IServiceCollection services)
.InitDatabaseForIdGenerator();
}
```

### Converters

KORM supports custom converters (via [IConverter](https://kros-sk.github.io/Kros.Libs.Documentation/api/Kros.KORM/Kros.KORM.Converter.IConverter.html) implementation) for converting values from DB to objects and vice versa. These converters are set for individual columns in `OnModelCreating` method of database configuration class (`DatabaseConfigurationBase`).

If you need to convert text formatted as JSON from database to entity, you can use pre-defined converter - `JsonConverter`:

``` csharp
public override void OnModelCreating(ModelConfigurationBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.HasTableName(MyEntityTableName)
.Property(x => x.MySerializableProperty).UseConverter<JsonConverter>();
}
```
47 changes: 47 additions & 0 deletions src/Converters/JsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Kros.Utils;
using System;
using System.Text.Json;

namespace Kros.KORM.Converter
{
/// <summary>
/// Converter for converting JSON value from DB to entity.
/// </summary>
/// <seealso cref="IConverter" />
public class JsonConverter<T> : IConverter
{
private readonly JsonSerializerOptions _options;

/// <summary>
/// Initializes a new instance of the <see cref="JsonConverter{T}"/> class with default serialization options.
/// </summary>
public JsonConverter() : this(new JsonSerializerOptions())
{ }

/// <summary>
/// Initializes a new instance of the <see cref="JsonConverter{T}"/> class.
/// </summary>
/// <param name="options">Serialization options.</param>
/// <exception cref="ArgumentNullException">The value of <paramref name="options"/> is null.</exception>
public JsonConverter(JsonSerializerOptions options)
{
_options = Check.NotNull(options, nameof(options));
}

/// <summary>
/// Converts specified JSON value from DB to entity.
/// </summary>
/// <param name="value">JSON value.</param>
/// <returns>Entity.</returns>
public object Convert(object value)
=> JsonSerializer.Deserialize((string)value, typeof(T), _options);

/// <summary>
/// Converts entity to JSON value for DB.
/// </summary>
/// <param name="value">Entity.</param>
/// <returns>Converted JSON value for DB.</returns>
public object ConvertBack(object value)
=> JsonSerializer.Serialize(value, typeof(T), _options);
}
}
1 change: 1 addition & 0 deletions src/Kros.KORM.Extensions.Asp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="System.Text.Json" Version="4.6.0" />
</ItemGroup>

<ItemGroup>
Expand Down
127 changes: 127 additions & 0 deletions tests/Converters/JsonConverterShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using FluentAssertions;
using Kros.KORM.Converter;
using System;
using System.Text.Json;
using System.Text.RegularExpressions;
using Xunit;

namespace Kros.KORM.Extensions.Api.UnitTests.Converters
{
public class JsonConverterShould
{
[Fact]
public void ThrowArgumentExceptionWhenSerializationOptionsAreNull()
{
Action action = () => new JsonConverter<TestClass>(null);

action.Should().Throw<ArgumentException>();
}

[Fact]
public void ConvertJsonToEntity()
{
var converter = new JsonConverter<TestClass>();

TestClass expected = GetSampleClass();
var actual = converter.Convert(GetSampleJson());

actual.Should().BeOfType(typeof(TestClass));
((TestClass)actual).Should().BeEquivalentTo(expected);
}

[Fact]
public void ThrowJsonExceptionWhenTryingToConvertImproperlyFormattedJsonToEntity()
{
var converter = new JsonConverter<TestClass>();

Action action = () => converter.Convert(GetSampleImproperlyFormattedJson());

action.Should().Throw<JsonException>();
}

[Fact]
public void ConvertImproperlyFormattedJsonToEntityWhenUsingCorrectOptions()
{
var converter = new JsonConverter<TestClass>(new JsonSerializerOptions()
{
AllowTrailingCommas = true,
PropertyNameCaseInsensitive = true
});

TestClass expected = GetSampleClass();
var actual = converter.Convert(GetSampleImproperlyFormattedJson());

actual.Should().BeOfType(typeof(TestClass));
((TestClass)actual).Should().BeEquivalentTo(expected);
}

[Fact]
public void ConvertEntityToJson()
{
var converter = new JsonConverter<TestClass>();

string expected = GetSampleJson();
var actual = converter.ConvertBack(GetSampleClass());

actual.Should().BeOfType(typeof(string));
((string)actual).Should().Equals(expected);
}

private TestClass GetSampleClass()
=> new TestClass()
{
BoolProperty = true,
StringProperty = "LoremIpsum",
DoubleProperty = Math.PI,
ArrayProperty = new int[] { 4, 8, 15, 16, 23, 42 },
ObjectProperty = new TestClass() { StringProperty = "DolorSitAmet" }
};

private string GetSampleJson()
=> Regex.Replace(
@"{
""BoolProperty"":true,
""StringProperty"":""LoremIpsum"",
""DoubleProperty"":3.1415926535897931,
""ArrayProperty"":[4,8,15,16,23,42],
""ObjectProperty"":
{
""BoolProperty"":false,
""StringProperty"":""DolorSitAmet"",
""DoubleProperty"":0,
""ArrayProperty"":null,
""ObjectProperty"":null
}
}", @"\s+", "");

private string GetSampleImproperlyFormattedJson()
=> Regex.Replace(
@"{
""boolProperty"":true,
""stringProperty"":""LoremIpsum"",
""doubleProperty"":3.1415926535897931,
""arrayProperty"":[4,8,15,16,23,42],
""objectProperty"":
{
""boolProperty"":false,
""stringProperty"":""DolorSitAmet"",
""doubleProperty"":0,
""arrayProperty"":null,
""objectProperty"":null
},
}", @"\s+", "");

private class TestClass
{
public bool BoolProperty { get; set; }

public string StringProperty { get; set; }

public double DoubleProperty { get; set; }

public int[] ArrayProperty { get; set; }

public TestClass ObjectProperty { get; set; }
}
}
}

0 comments on commit de6e67a

Please sign in to comment.