Created
November 16, 2023 07:55
-
-
Save VictorKoenders/5345d5eaef21629e3bb16c3750cb5818 to your computer and use it in GitHub Desktop.
[JsonMerge] attribute
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] | |
public class JsonMergeAttribute : Attribute | |
{ | |
public JsonMergeAttribute(string propertyName) | |
{ | |
PropertyName = propertyName; | |
} | |
public string PropertyName { get; } | |
} | |
public class JsonMergeConverter : JsonConverter<object> | |
{ | |
public override bool CanConvert(Type typeToConvert) | |
{ | |
return typeToConvert.GetCustomAttribute<JsonMergeAttribute>() != null; | |
} | |
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | |
{ | |
throw new NotImplementedException(); | |
} | |
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) | |
{ | |
var attribute = value.GetType().GetCustomAttribute<JsonMergeAttribute>() ?? throw new Exception(); | |
var members = value.GetType().GetProperties(); | |
object? childObject = null; | |
writer.WriteStartObject(); | |
foreach (var member in members) | |
{ | |
if (member.Name == attribute.PropertyName) | |
{ | |
childObject = member.GetValue(value); | |
continue; | |
} | |
writer.WritePropertyName(member.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name ?? member.Name); | |
JsonSerializer.Serialize(writer, member.GetValue(value), member.PropertyType, options); | |
} | |
if (childObject == null) throw new Exception("Field \"" + attribute.PropertyName + "\" not found"); | |
var obj = JsonSerializer.SerializeToDocument(childObject, options); | |
foreach (var kv in obj.RootElement.EnumerateObject()) | |
{ | |
writer.WritePropertyName(kv.Name); | |
kv.Value.WriteTo(writer); | |
} | |
writer.WriteEndObject(); | |
} | |
} | |
public class JsonMergeSchemaFilter : ISchemaFilter | |
{ | |
public void Apply(OpenApiSchema schema, SchemaFilterContext context) | |
{ | |
var attribute = context.Type.GetCustomAttribute<JsonMergeAttribute>(); | |
if (attribute == null) return; | |
var property = schema.Properties.First(p => string.Equals(attribute.PropertyName, p.Key, StringComparison.InvariantCultureIgnoreCase)); | |
schema.Properties.Remove(property.Key); | |
Copy(schema.Properties, property.Value.Properties); | |
Copy(schema.AnyOf, property.Value.AnyOf); | |
Copy(schema.AllOf, property.Value.AllOf); | |
} | |
private static void Copy(IList<OpenApiSchema> dest, IList<OpenApiSchema> src) | |
{ | |
foreach (var s in src) | |
dest.Add(s); | |
} | |
private static void Copy(IDictionary<string, OpenApiSchema> dest, IDictionary<string, OpenApiSchema> src) | |
{ | |
foreach (var kv in src) | |
dest.Add(kv.Key, kv.Value); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Merges the contents of the `Data` field into the parent object | |
[JsonMerge(nameof(Data))] | |
public class IdRev<T> | |
{ | |
public Guid Id { get; set; } | |
public string Rev { get; set; } | |
public T Data { get; set; } | |
} | |
/* example: | |
record Foo(string Bar, string Baz); | |
// IdRev<Foo>: | |
{ | |
"id": "...", | |
"rev": "...", | |
"bar": "...", | |
"baz": "..." | |
} | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
builder.Services.AddControllers() | |
.AddJsonOptions(options => | |
{ | |
options.JsonSerializerOptions.Converters.Add(new JsonMergeConverter()); | |
}); | |
builder.Services.AddSwaggerGen(options => | |
{ | |
options.SchemaFilter<JsonMergeSchemaFilter>(); | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment