Skip to content

Commit

Permalink
WIP Migrating from Newtonsoft.Json to System.Text.Json
Browse files Browse the repository at this point in the history
  • Loading branch information
kav committed Sep 3, 2024
1 parent 9b33b65 commit 56ee4d6
Show file tree
Hide file tree
Showing 20 changed files with 422 additions and 298 deletions.
6 changes: 3 additions & 3 deletions Postgrest/Attributes/ColumnAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace Supabase.Postgrest.Attributes
{

Expand All @@ -26,7 +26,7 @@ public class ColumnAttribute : Attribute
/// <summary>
/// Specifies what should be serialized in the event this column's value is NULL
/// </summary>
public NullValueHandling NullValueHandling { get; set; }
public JsonIgnoreCondition NullValueHandling { get; set; }

/// <summary>
/// If the performed query is an Insert or Upsert, should this value be ignored?
Expand All @@ -39,7 +39,7 @@ public class ColumnAttribute : Attribute
public bool IgnoreOnUpdate { get; }

/// <inheritdoc />
public ColumnAttribute([CallerMemberName] string? columnName = null, NullValueHandling nullValueHandling = NullValueHandling.Include, bool ignoreOnInsert = false, bool ignoreOnUpdate = false)
public ColumnAttribute([CallerMemberName] string? columnName = null, JsonIgnoreCondition nullValueHandling = JsonIgnoreCondition.Never, bool ignoreOnInsert = false, bool ignoreOnUpdate = false)
{
ColumnName = columnName!; // Will either be user specified or given by runtime compiler.
NullValueHandling = nullValueHandling;
Expand Down
46 changes: 22 additions & 24 deletions Postgrest/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Text.Json;
using System.Text.Json.Serialization;
using Supabase.Core.Extensions;
using Supabase.Postgrest.Interfaces;
using Supabase.Postgrest.Models;
using Supabase.Postgrest.Responses;
using System.Text.Json.Serialization.Metadata;
using System.Reflection;
using Supabase.Postgrest.Converters;

namespace Supabase.Postgrest
{
/// <inheritdoc />
public class Client : IPostgrestClient
{
/// <summary>
/// Custom Serializer resolvers and converters that will be used for encoding and decoding Postgrest JSON responses.
/// Custom Serializer options that will be used for encoding and decoding Postgrest JSON responses.
///
/// By default, Postgrest seems to use a date format that C# and Newtonsoft do not like, so this initial
/// By default, Postgrest seems to use a date format that C# does not like, so this initial
/// configuration handles that.
/// </summary>
public static JsonSerializerSettings SerializerSettings(ClientOptions? options = null)
public static JsonSerializerOptions SerializerOptions(ClientOptions? options = null)
{
options ??= new ClientOptions();

return new JsonSerializerSettings
return new JsonSerializerOptions
{
ContractResolver = new PostgrestContractResolver(),
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
Converters =
{
// 2020-08-28T12:01:54.763231
new IsoDateTimeConverter
{
DateTimeStyles = options.DateTimeStyles,
DateTimeFormat = ClientOptions.DATE_TIME_FORMAT
}
new JsonStringEnumConverter(),
// new PostgrestDateTimeConverter(options.DateTimeStyles, ClientOptions.DATE_TIME_FORMAT)
new RangeConverter()
}
};
}
Expand Down Expand Up @@ -70,7 +70,7 @@ public void RemoveDebugHandler(IPostgrestDebugger.DebugEventHandler handler) =>

/// <summary>
/// Function that can be set to return dynamic headers.
///
///
/// Headers specified in the constructor options will ALWAYS take precedence over headers returned by this function.
/// </summary>
public Func<Dictionary<string, string>>? GetHeaders { get; set; }
Expand All @@ -87,29 +87,27 @@ public Client(string baseUrl, ClientOptions? options = null)
Options = options ?? new ClientOptions();
}


/// <inheritdoc />
public IPostgrestTable<T> Table<T>() where T : BaseModel, new() =>
new Table<T>(BaseUrl, SerializerSettings(Options), Options)
new Table<T>(BaseUrl, SerializerOptions(Options), Options)
{
GetHeaders = GetHeaders
};

/// <inheritdoc />
public IPostgrestTableWithCache<T> Table<T>(IPostgrestCacheProvider cacheProvider)
where T : BaseModel, new() =>
new TableWithCache<T>(BaseUrl, cacheProvider, SerializerSettings(Options), Options)
new TableWithCache<T>(BaseUrl, cacheProvider, SerializerOptions(Options), Options)
{
GetHeaders = GetHeaders
};


/// <inheritdoc />
public async Task<TModeledResponse?> Rpc<TModeledResponse>(string procedureName, object? parameters = null)
{
var response = await Rpc(procedureName, parameters);

return string.IsNullOrEmpty(response.Content) ? default : JsonConvert.DeserializeObject<TModeledResponse>(response.Content!);
return string.IsNullOrEmpty(response.Content) ? default : JsonSerializer.Deserialize<TModeledResponse>(response.Content!, SerializerOptions(Options));
}

/// <inheritdoc />
Expand All @@ -120,13 +118,13 @@ public Task<BaseResponse> Rpc(string procedureName, object? parameters = null)

var canonicalUri = builder.Uri.ToString();

var serializerSettings = SerializerSettings(Options);
var serializerOptions = SerializerOptions(Options);

// Prepare parameters
Dictionary<string, object>? data = null;
if (parameters != null)
data = JsonConvert.DeserializeObject<Dictionary<string, object>>(
JsonConvert.SerializeObject(parameters, serializerSettings));
data = JsonSerializer.Deserialize<Dictionary<string, object>>(
JsonSerializer.Serialize(parameters, serializerOptions));

// Prepare headers
var headers = Helpers.PrepareRequestHeaders(HttpMethod.Post,
Expand All @@ -137,8 +135,8 @@ public Task<BaseResponse> Rpc(string procedureName, object? parameters = null)

// Send request
var request =
Helpers.MakeRequest(Options, HttpMethod.Post, canonicalUri, serializerSettings, data, headers);
Helpers.MakeRequest(Options, HttpMethod.Post, canonicalUri, serializerOptions, data, headers);
return request;
}
}
}
}
75 changes: 42 additions & 33 deletions Postgrest/Converters/DateTimeConverter.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Supabase.Postgrest.Converters
{

/// <inheritdoc />
public class DateTimeConverter : JsonConverter
public class DateTimeConverter : JsonConverter<object>
{
/// <inheritdoc />
public override bool CanConvert(Type objectType)
public override bool CanConvert(Type typeToConvert)
{
throw new NotImplementedException();
return typeToConvert == typeof(DateTime) || typeToConvert == typeof(List<DateTime>);
}

/// <inheritdoc />
public override bool CanWrite => false;

/// <inheritdoc />
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.Value != null)
if (reader.TokenType == JsonTokenType.String)
{
var str = reader.Value.ToString();
var str = reader.GetString();
if (str == null)
{
return null;
}

var infinity = ParseInfinity(str);

Expand All @@ -32,38 +33,46 @@ public override bool CanConvert(Type objectType)
return (DateTime)infinity;
}

var date = DateTime.Parse(str);
return date;
return DateTime.Parse(str);
}

var result = new List<DateTime>();

try
if (reader.TokenType == JsonTokenType.StartArray)
{
var jo = JArray.Load(reader);
var result = new List<DateTime>();

foreach (var item in jo.ToArray())
while (reader.Read())
{
var inner = item.ToString();

var infinity = ParseInfinity(inner);

if (infinity != null)
if (reader.TokenType == JsonTokenType.EndArray)
{
result.Add((DateTime)infinity);
break;
}

var date = DateTime.Parse(inner);
result.Add(date);
if (reader.TokenType == JsonTokenType.String)
{
var inner = reader.GetString();
if (inner == null)
{
return null;
}

var infinity = ParseInfinity(inner);

if (infinity != null)
{
result.Add((DateTime)infinity);
}
else
{
var date = DateTime.Parse(inner);
result.Add(date);
}
}
}
}
catch (JsonReaderException)
{
return null;
}

return result;
}

return result;
return null;
}

private static DateTime? ParseInfinity(string input)
Expand All @@ -77,7 +86,7 @@ public override bool CanConvert(Type objectType)
}

/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
Expand Down
47 changes: 33 additions & 14 deletions Postgrest/Converters/IntConverter.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Supabase.Postgrest.Converters
{

/// <inheritdoc />
public class IntArrayConverter : JsonConverter
public class IntArrayConverter : JsonConverter<List<int>>
{
/// <inheritdoc />
public override bool CanConvert(Type objectType)
public override bool CanConvert(Type typeToConvert)
{
throw new NotImplementedException();
return typeToConvert == typeof(List<int>);
}

/// <inheritdoc />
public override bool CanRead => false;

/// <inheritdoc />
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
public override List<int>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
if (reader.TokenType != JsonTokenType.StartArray)
{
throw new JsonException("Expected start of array.");
}

var list = new List<int>();

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
{
return list;
}

if (reader.TokenType == JsonTokenType.Number)
{
list.Add(reader.GetInt32());
}
else
{
throw new JsonException($"Unexpected token type: {reader.TokenType}");
}
}

throw new JsonException("Unexpected end of JSON.");
}

/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, List<int> value, JsonSerializerOptions options)
{
if (value is List<int> list)
{
writer.WriteValue($"{{{string.Join(",", list)}}}");
}
writer.WriteStringValue($"{{{string.Join(",", value)}}}");
}
}
}
28 changes: 15 additions & 13 deletions Postgrest/Converters/RangeConverter.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
using System;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Supabase.Postgrest.Extensions;
using Supabase.Postgrest.Exceptions;

namespace Supabase.Postgrest.Converters
{

/// <summary>
/// Used by Newtonsoft.Json to convert a C# range into a Postgrest range.
/// Used by System.Text.Json to convert a C# range into a Postgrest range.
/// </summary>
internal class RangeConverter : JsonConverter
internal class RangeConverter : JsonConverter<IntRange>
{
public override bool CanConvert(Type objectType)
public override bool CanConvert(Type typeToConvert)
{
throw new NotImplementedException();
return typeToConvert == typeof(IntRange);
}

public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
public override IntRange? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.Value != null ? ParseIntRange(reader.Value.ToString()) : null;
if (reader.TokenType == JsonTokenType.String)
{
string? value = reader.GetString();
return value != null ? ParseIntRange(value) : null;
}
throw new JsonException("Expected string value for IntRange.");
}

public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, IntRange value, JsonSerializerOptions options)
{
if (value == null) return;

var val = (IntRange)value;
writer.WriteValue(val.ToPostgresString());
writer.WriteStringValue(value.ToPostgresString());
}

public static IntRange ParseIntRange(string value)
Expand Down
Loading

0 comments on commit 56ee4d6

Please sign in to comment.