Support IgnoreUnmatchedProperties in YAML serialization (#574)
* Support IgnoreUnmatchedProperties in YAML serialization * Remove unnecessary null-conditional operator
This commit is contained in:
committed by
GitHub
parent
a4350d6c8f
commit
d48e93c1f6
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user