Skip to content

Commit

Permalink
Nullability fixes (#3)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Butler <andrew.butler@boomin.com>
  • Loading branch information
AButler and Andrew Butler authored Jul 29, 2022
1 parent c67366a commit 7c93b99
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 97 deletions.
36 changes: 17 additions & 19 deletions HttpPatch.Schema/EnumMemberJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,24 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace HttpPatch.Schema
{
internal class EnumMemberJsonConverter<T> : JsonConverter<T>
where T : Enum
{
private static readonly IDictionary<T, string> ValueMap = Enum
.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(v => v, v => typeof(T).GetField(v.ToString())?.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? v.ToString());
namespace HttpPatch.Schema;

public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var str = reader.GetString();
return ValueMap.FirstOrDefault(kv => string.Equals(kv.Value, str, StringComparison.InvariantCultureIgnoreCase)).Key;
}
internal class EnumMemberJsonConverter<T> : JsonConverter<T>
where T : Enum
{
private static readonly IDictionary<T, string> ValueMap = Enum
.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(v => v, v => typeof(T).GetField(v.ToString())?.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? v.ToString());

[SuppressMessage("", "CA1062", Justification = "Writer never be null.")]
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
writer.WriteStringValue(ValueMap[value]);
}
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var str = reader.GetString();
return ValueMap.FirstOrDefault(kv => string.Equals(kv.Value, str, StringComparison.InvariantCultureIgnoreCase)).Key;
}

public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
writer.WriteStringValue(ValueMap[value]);
}
}
13 changes: 6 additions & 7 deletions HttpPatch.Schema/PatchDocument.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations;

namespace HttpPatch.Schema
namespace HttpPatch.Schema;

public class PatchDocument
{
public class PatchDocument
{
[MinLength(1)]
[Required]
public IEnumerable<PatchOperation> Operations { get; set; }
}
[MinLength(1)]
[Required]
public IEnumerable<PatchOperation> Operations { get; set; } = null!;
}
66 changes: 33 additions & 33 deletions HttpPatch.Schema/PatchOperation.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,50 @@
using System.Buffers;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace HttpPatch.Schema
namespace HttpPatch.Schema;

public class PatchOperation
{
public class PatchOperation
{
[JsonPropertyName("op")]
[JsonConverter(typeof(EnumMemberJsonConverter<PatchOperationType>))]
public PatchOperationType Type { get; set; }
[JsonPropertyName("op")]
[JsonConverter(typeof(EnumMemberJsonConverter<PatchOperationType>))]
[Required]
public PatchOperationType Type { get; set; }

public string Path { get; set; }
[Required]
public string Path { get; set; } = null!;

public object Value { get; set; }
public object? Value { get; set; }

public T ValueAs<T>(T defaultValue = default)
public T? ValueAs<T>(T? defaultValue = default)
{
if (Value == null)
{
if (Value == null)
{
return defaultValue;
}
return defaultValue;
}

if (!(Value is JsonElement))
{
throw new JsonException("Value is not an JsonElement.");
}
if (Value is not JsonElement valueElement)
{
throw new JsonException("Value is not an JsonElement.");
}

var element = (JsonElement)Value;

if (typeof(T).IsEnum)
{
var value = element.GetString();
if (typeof(T).IsEnum)
{
var value = valueElement.GetString();

if (!Enum.TryParse(typeof(T), value, true, out var enumValue))
{
throw new PatchOperationValidationException($"'{value}' is not a valid value for {typeof(T).Name}");
}
return (T)enumValue;
if (!Enum.TryParse(typeof(T), value, true, out var enumValue))
{
throw new PatchOperationValidationException($"'{value}' is not a valid value for {typeof(T).Name}");
}
return (T?)enumValue;
}

var bufferWriter = new ArrayBufferWriter<byte>(1024);
using var writer = new Utf8JsonWriter(bufferWriter);
element.WriteTo(writer);
writer.Flush();
var bufferWriter = new ArrayBufferWriter<byte>(1024);
using var writer = new Utf8JsonWriter(bufferWriter);
valueElement.WriteTo(writer);
writer.Flush();

return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase});
}
return JsonSerializer.Deserialize<T?>(bufferWriter.WrittenSpan, new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase});
}
}
11 changes: 5 additions & 6 deletions HttpPatch.Schema/PatchOperationType.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
namespace HttpPatch.Schema
namespace HttpPatch.Schema;

public enum PatchOperationType
{
public enum PatchOperationType
{
Remove,
Replace
}
Remove,
Replace
}
35 changes: 17 additions & 18 deletions HttpPatch.Schema/PatchOperationValidationException.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
using System.Runtime.Serialization;

namespace HttpPatch.Schema
namespace HttpPatch.Schema;

[Serializable]
public class PatchOperationValidationException : Exception
{
[Serializable]
public class PatchOperationValidationException : Exception
public PatchOperationValidationException()
{
public PatchOperationValidationException()
{
}
}

public PatchOperationValidationException(string message) : base(message)
{
}
public PatchOperationValidationException(string message) : base(message)
{
}

public PatchOperationValidationException(string message, Exception inner) : base(message, inner)
{
}
public PatchOperationValidationException(string message, Exception inner) : base(message, inner)
{
}

protected PatchOperationValidationException(
SerializationInfo info,
StreamingContext context
) : base(info, context)
{
}
protected PatchOperationValidationException(
SerializationInfo info,
StreamingContext context
) : base(info, context)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public IActionResult Patch(string resourceId, [FromBody] TestResourcePatchReques

if (request.Count.IsIncludedInPatch)
{
testResource = testResource with { Count = request.Count.Value! };
testResource = testResource with { Count = request.Count.Value };
}

if (request.Address.IsIncludedInPatch)
Expand All @@ -39,7 +39,7 @@ public IActionResult Patch(string resourceId, [FromBody] TestResourcePatchReques

if (request.State.IsIncludedInPatch)
{
testResource = testResource with { State = request.State.Value! };
testResource = testResource with { State = request.State.Value };
}

if (request.People.IsIncludedInPatch)
Expand Down
5 changes: 4 additions & 1 deletion HttpPatch.Tests/HttpClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ internal static class HttpClientExtensions
{
public static Task<HttpResponseMessage> PatchAsJsonAsync<T>(this HttpClient client, string requestUri, T body)
{
if (client == null) { throw new ArgumentNullException(nameof(client)); }
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}

var httpRequestMessage = new HttpRequestMessage
{
Expand Down
16 changes: 5 additions & 11 deletions HttpPatch/OptionallyPatchedJsonSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
using System.Diagnostics.CodeAnalysis;

namespace HttpPatch;

[SuppressMessage("", "CA1034", Justification = "Nicer to group the 2 serializers together")]
internal static class OptionallyPatchedJsonSerializer
{
[SuppressMessage("", "CA1812", Justification = "Used in attributes")]
internal class NewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter<IOptionallyPatched>
{
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, IOptionallyPatched? value, Newtonsoft.Json.JsonSerializer serializer)
Expand All @@ -20,7 +16,7 @@ public override void WriteJson(Newtonsoft.Json.JsonWriter writer, IOptionallyPat
}
}

public override IOptionallyPatched? ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, IOptionallyPatched? existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
public override IOptionallyPatched ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, IOptionallyPatched? existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
{
var underlyingType = objectType.GetGenericArguments().Single();

Expand All @@ -33,9 +29,8 @@ public override void WriteJson(Newtonsoft.Json.JsonWriter writer, IOptionallyPat
return (IOptionallyPatched)Activator.CreateInstance(objectType, true, deserializedItem)!;
}
}

[SuppressMessage("", "CA1812", Justification = "Used in attributes")]
internal class SystemTextJson<T> : System.Text.Json.Serialization.JsonConverter<OptionallyPatched<T>>

private class SystemTextJson<T> : System.Text.Json.Serialization.JsonConverter<OptionallyPatched<T>>
{
public override void Write(System.Text.Json.Utf8JsonWriter writer, OptionallyPatched<T> value, System.Text.Json.JsonSerializerOptions options)
=> throw new NotImplementedException();
Expand All @@ -48,14 +43,13 @@ public override OptionallyPatched<T> Read(ref System.Text.Json.Utf8JsonReader re
return (OptionallyPatched<T>)optionallyPatchedWrapper!;
}
}

[SuppressMessage("", "CA1812", Justification = "Used in attributes")]

internal class SystemTextJsonFactory : System.Text.Json.Serialization.JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
=> typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(OptionallyPatched<>);

public override System.Text.Json.Serialization.JsonConverter? CreateConverter(Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
public override System.Text.Json.Serialization.JsonConverter CreateConverter(Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
{
var underlyingType = typeToConvert.GetGenericArguments().Single();
var genericType = typeof(SystemTextJson<>).MakeGenericType(underlyingType);
Expand Down

0 comments on commit 7c93b99

Please sign in to comment.