Hello
I have a solution that proposes a System.Text.Json
JsonConverter. It is using reflection, so I would not recommend using this code other than on a WebApi solution. Reflection may be slow on mobile platforms.
Solution
// An abstract class for your items
public abstract class StrapiItem<T>
where T : StrapiItem<T>
{
public int Id { get; set; }
public string Locale { get; set; }
public DateTime? CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public DateTime? PublishedAt { get; set; }
public StrapiListWrapper<T> Localizations { get; set; }
}
// A wrapper for your lists
public class StrapiListWrapper<T>
where T : StrapiItem<T>
{
public IEnumerable<T> Data { get; set; }
public Meta Meta { get; set; }
}
// A JsonConverter implementation for a StrapiItem<T>
public class StrapiItemConverter<T> : JsonConverter<T>
where T : StrapiItem<T>, new()
{
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string? propertyName = null;
var item = new T();
var properties = typeToConvert.GetProperties();
var initialDepth = reader.CurrentDepth;
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
propertyName = reader.GetString();
}
else if (!string.IsNullOrWhiteSpace(propertyName))
{
if (propertyName == "attributes" || reader.TokenType == JsonTokenType.Null)
{
continue;
}
var property = properties.SingleOrDefault(property => property.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
var setMethod = property?.GetSetMethod();
if (setMethod != null)
{
object? value = null;
if (typeof(int).IsAssignableTo(property!.PropertyType))
{
value = reader.GetInt32();
}
else if (typeof(string).IsAssignableTo(property!.PropertyType))
{
value = reader.GetString();
}
else if (typeof(DateTime).IsAssignableTo(property!.PropertyType))
{
value = reader.GetDateTime();
}
else if (typeof(DateTimeOffset).IsAssignableTo(property!.PropertyType))
{
value = reader.GetDateTimeOffset();
}
else if (typeof(bool).IsAssignableTo(property!.PropertyType))
{
value = reader.GetBoolean();
}
else if (typeof(decimal).IsAssignableTo(property!.PropertyType))
{
value = reader.GetDecimal();
}
else if (typeof(double).IsAssignableTo(property!.PropertyType))
{
value = reader.GetDouble();
}
else if (typeof(Guid).IsAssignableTo(property!.PropertyType))
{
value = reader.GetGuid();
}
else if (property.PropertyType.IsEnum || Nullable.GetUnderlyingType(property.PropertyType) != null)
{
var enumValue = reader.GetString();
if (!string.IsNullOrWhiteSpace(enumValue))
{
value = Enum.Parse(property.PropertyType, enumValue!, ignoreCase: true);
}
}
else
{
using (var jsonDocument = JsonDocument.ParseValue(ref reader))
{
var json = jsonDocument.RootElement.GetRawText();
value = JsonSerializer.Deserialize(json, property.PropertyType, options);
}
}
if (value != null)
{
setMethod.Invoke(item, new[] { value });
}
}
propertyName = null;
}
else if (reader.TokenType == JsonTokenType.EndObject && reader.CurrentDepth == initialDepth)
{
break;
}
}
return item;
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
}
Usage
Single items
Now, when strapi returns a single item like this…
{
"data": {
"id": 1,
"attributes": {
"title": "foo",
"createdAt": "2023-04-19T00:20:53.175Z",
"updatedAt": "2023-04-21T17:28:45.603Z",
"publishedAt": "2023-04-19T00:24:22.336Z",
"locale": "en",
}
},
"meta": {}
}
… you can deserialize it like this :
var jsonSerializerOptions = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
var data = JsonSerializer.Deserialize<StrapiItemWrapper<Data>>(json, options);
And the data class would look something like this :
[JsonConverter(typeof(StrapiItemConverter<Data>))]
public class Data : StrapiItem<Data>
{
public string Title { get; set; }
}
Lists
When strapi returns a list like this…
{
"data": [
{
"id": 1,
"attributes": {
"title": "Mitre saws",
"createdAt": "2023-04-19T00:20:53.175Z",
"updatedAt": "2023-04-21T17:28:45.603Z",
"publishedAt": "2023-04-19T00:24:22.336Z",
"locale": "en",
}
}
]
}
…you can deserialize it like this :
var jsonSerializerOptions = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
var data = JsonSerializer.Deserialize<StrapiListWrapper<Data>>(json, options);
I’m thinking of wrapping this into a nuget package or maybe make a pull request on this repo to help the .NET community use Strapi.
Let me know in the comments if you would be interested!