Skip to content

Instantly share code, notes, and snippets.

@vbilopav
Last active May 23, 2023 11:11
Show Gist options
  • Save vbilopav/c0dcb26c582025d7e07c7c9116150490 to your computer and use it in GitHub Desktop.
Save vbilopav/c0dcb26c582025d7e07c7c9116150490 to your computer and use it in GitHub Desktop.
Utility function in C# which validates JSON string against .NET model type by using Newtonsoft Json library.
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace MyNamespace;
public static class JsonValidator
{
public static bool IsJsonValidType(this string json, Type modelType, out string? message)
{
try
{
var obj = JObject.Parse(json);
foreach (var prop in modelType.GetProperties())
{
var name = prop.Name.ConvertToCamelCase();
Type type = prop.PropertyType;
var value = obj[name];
if (value is null)
{
message = $"Missing field name: {name}";
return false;
}
message = value.Type switch
{
JTokenType.Array when type.IsListType() || type.IsArray => ValidateList(),
JTokenType.Integer when type == typeof(int) || type == typeof(int?) => null,
JTokenType.String when type == typeof(string) => null,
JTokenType.Boolean when type == typeof(bool) || type == typeof(bool?) => null,
JTokenType.Null when type.IsNullableType() => null,
JTokenType.Object when type.IsClass => ValidateObject(),
_ => $"Field {name} is expected to be of the type {type.Name}, found type: {value.Type.ToString().ConvertToCamelCase()}",
};
if (message is not null)
{
return false;
}
string? ValidateList()
{
var genericArgs = type.GetGenericArguments();
var listType = genericArgs.Length > 0 ? type.GetGenericArguments()[0] : type.GetElementType()!;
foreach (var item in value)
{
if (item.Type == JTokenType.String && listType != typeof(string))
{
return $"Field {name} is expected to be a list of strings.";
}
else if (item.Type == JTokenType.Object)
{
if (!item.ToString().IsJsonValidType(listType, out var msg))
{
return msg;
}
}
}
return null;
}
string? ValidateObject()
{
if (value != null && !value.ToString().IsJsonValidType(type, out var msg))
{
return msg;
}
return null;
}
}
message = null;
return true;
}
catch (Exception ex)
{
message = ex.Message;
return false;
}
}
private static bool IsNullableType(this Type type) => type == typeof(string) || type.IsClass
? true
: type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
private static bool IsListType(this Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
private static string ConvertToCamelCase(this string name) => CamelCaseResolver.Resolve(name);
private class CamelCaseResolver : CamelCaseNamingStrategy
{
private static readonly CamelCaseResolver instance = new();
public static string Resolve(string name) => instance.ResolvePropertyName(name);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment