Support IgnoreUnmatchedProperties in YAML serialization (#574)

* Support IgnoreUnmatchedProperties in YAML serialization

* Remove unnecessary null-conditional operator
This commit is contained in:
Alex Meyer-Gleaves
2021-03-03 06:35:20 +10:00
committed by GitHub
parent a4350d6c8f
commit d48e93c1f6
4 changed files with 181 additions and 124 deletions

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Core;
@@ -18,6 +19,26 @@ namespace k8s
/// </summary>
public static class Yaml
{
private static readonly IDeserializer Deserializer =
new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeConverter(new IntOrStringYamlConverter())
.WithTypeConverter(new ByteArrayStringYamlConverter())
.WithOverridesFromJsonPropertyAttributes()
.IgnoreUnmatchedProperties()
.Build();
private static readonly IValueSerializer Serializer =
new SerializerBuilder()
.DisableAliases()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeConverter(new IntOrStringYamlConverter())
.WithTypeConverter(new ByteArrayStringYamlConverter())
.WithEventEmitter(e => new StringQuotingEmitter(e))
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
.WithOverridesFromJsonPropertyAttributes()
.BuildValueSerializer();
public class ByteArrayStringYamlConverter : IYamlTypeConverter
{
public bool Accepts(Type type)
@@ -105,30 +126,15 @@ namespace k8s
throw new ArgumentNullException(nameof(typeMap));
}
var deserializer =
new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.WithTypeConverter(new ByteArrayStringYamlConverter())
.IgnoreUnmatchedProperties()
.Build();
var types = new List<Type>();
var parser = new Parser(new StringReader(content));
parser.Consume<StreamStart>();
while (parser.Accept<DocumentStart>(out _))
{
var obj = deserializer.Deserialize<KubernetesObject>(parser);
var obj = Deserializer.Deserialize<KubernetesObject>(parser);
types.Add(typeMap[obj.ApiVersion + "/" + obj.Kind]);
}
deserializer =
new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.WithTypeConverter(new ByteArrayStringYamlConverter())
.Build();
parser = new Parser(new StringReader(content));
parser.Consume<StreamStart>();
var ix = 0;
@@ -136,7 +142,7 @@ namespace k8s
while (parser.Accept<DocumentStart>(out _))
{
var objType = types[ix++];
var obj = deserializer.Deserialize(parser, objType);
var obj = Deserializer.Deserialize(parser, objType);
results.Add(obj);
}
@@ -160,14 +166,7 @@ namespace k8s
public static T LoadFromString<T>(string content)
{
var deserializer =
new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.WithTypeConverter(new ByteArrayStringYamlConverter())
.Build();
var obj = deserializer.Deserialize<T>(content);
var obj = Deserializer.Deserialize<T>(content);
return obj;
}
@@ -177,119 +176,44 @@ namespace k8s
var writer = new StringWriter(stringBuilder);
var emitter = new Emitter(writer);
var serializer =
new SerializerBuilder()
.DisableAliases()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.WithTypeConverter(new ByteArrayStringYamlConverter())
.WithEventEmitter(e => new StringQuotingEmitter(e))
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
.BuildValueSerializer();
emitter.Emit(new StreamStart());
emitter.Emit(new DocumentStart());
serializer.SerializeValue(emitter, value, typeof(T));
Serializer.SerializeValue(emitter, value, typeof(T));
return stringBuilder.ToString();
}
private class AutoRestTypeInspector : ITypeInspector
private static TBuilder WithOverridesFromJsonPropertyAttributes<TBuilder>(this TBuilder builder)
where TBuilder : BuilderSkeleton<TBuilder>
{
private readonly ITypeInspector _inner;
// Use VersionInfo from the model namespace as that should be stable.
// If this is not generated in the future we will get an obvious compiler error.
var targetNamespace = typeof(VersionInfo).Namespace;
public AutoRestTypeInspector(ITypeInspector inner)
{
_inner = inner;
}
// Get all the concrete model types from the code generated namespace.
var types = typeof(KubernetesEntityAttribute).Assembly
.ExportedTypes
.Where(type => type.Namespace == targetNamespace &&
!type.IsInterface &&
!type.IsAbstract);
public IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
// Map any JsonPropertyAttribute instances to YamlMemberAttribute instances.
foreach (var type in types)
{
var pds = _inner.GetProperties(type, container);
return pds.Select(pd => TrimPropertySuffix(pd, type)).ToList();
}
public IPropertyDescriptor GetProperty(Type type, object container, string name, bool ignoreUnmatched)
{
try
foreach (var property in type.GetProperties())
{
return _inner.GetProperty(type, container, name, ignoreUnmatched);
}
catch (System.Runtime.Serialization.SerializationException)
{
return _inner.GetProperty(type, container, name + "Property", ignoreUnmatched);
var jsonAttribute = property.GetCustomAttribute<JsonPropertyAttribute>();
if (jsonAttribute == null)
{
continue;
}
var yamlAttribute = new YamlMemberAttribute { Alias = jsonAttribute.PropertyName };
builder.WithAttributeOverride(type, property.Name, yamlAttribute);
}
}
private IPropertyDescriptor TrimPropertySuffix(IPropertyDescriptor pd, Type type)
{
if (!pd.Name.EndsWith("Property", StringComparison.InvariantCulture))
{
return pd;
}
// This might have been renamed by AutoRest. See if there is a
// JsonPropertyAttribute.PropertyName and use that instead if there is.
var jpa = pd.GetCustomAttribute<JsonPropertyAttribute>();
if (jpa == null || string.IsNullOrEmpty(jpa.PropertyName))
{
return pd;
}
return new RenamedPropertyDescriptor(pd, jpa.PropertyName);
}
private class RenamedPropertyDescriptor : IPropertyDescriptor
{
private readonly IPropertyDescriptor _inner;
private readonly string _name;
public RenamedPropertyDescriptor(IPropertyDescriptor inner, string name)
{
_inner = inner;
_name = name;
}
public string Name => _name;
public bool CanWrite => _inner.CanWrite;
public Type Type => _inner.Type;
public Type TypeOverride
{
get => _inner.TypeOverride;
set => _inner.TypeOverride = value;
}
public int Order
{
get => _inner.Order;
set => _inner.Order = value;
}
public ScalarStyle ScalarStyle
{
get => _inner.ScalarStyle;
set => _inner.ScalarStyle = value;
}
public T GetCustomAttribute<T>()
where T : Attribute
{
return _inner.GetCustomAttribute<T>();
}
public IObjectDescriptor Read(object target)
{
return _inner.Read(target);
}
public void Write(object target, object value)
{
_inner.Write(target, value);
}
}
return builder;
}
}
}