Skip to content

Commit

Permalink
Enable serialization of public fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Antoine Aubry committed Feb 12, 2019
1 parent 650b303 commit b542ab9
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 5 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

# Changelog

## Version 5.4.0

New features:
* **Enable serialization of public fields.**
YamlDotNet will now also serialize public fields. This feature is enabled by default,
but it can be disabled by calling `IgnoreFields()` no the `SerializerBuilder` or `DeserializerBuilder`.

## Version 5.3.1

New features:
Expand Down
44 changes: 44 additions & 0 deletions YamlDotNet.Test/Serialization/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,50 @@ public void InfiniteRecursionIsDetected()
var exception = Assert.Throws<MaximumRecursionLevelReachedException>(() => sut.Serialize(recursionRoot));
}

[Fact]
public void TuplesAreSerializable()
{
var sut = new SerializerBuilder()
.Build();

var yaml = sut.Serialize(new[]
{
Tuple.Create(1, "one"),
Tuple.Create(2, "two"),
});

var expected = Yaml.Text(@"
- Item1: 1
Item2: one
- Item1: 2
Item2: two
");

Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines());
}

[Fact]
public void ValueTuplesAreSerializableWithoutMetadata()
{
var sut = new SerializerBuilder()
.Build();

var yaml = sut.Serialize(new[]
{
(num: 1, txt: "one"),
(num: 2, txt: "two"),
});

var expected = Yaml.Text(@"
- Item1: 1
Item2: one
- Item1: 2
Item2: two
");

Assert.Equal(expected.NormalizeNewLines(), yaml.NormalizeNewLines().TrimNewLines());
}

[TypeConverter(typeof(DoublyConvertedTypeConverter))]
public class DoublyConverted
{
Expand Down
5 changes: 5 additions & 0 deletions YamlDotNet.Test/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@ public static string NormalizeNewLines(this string value)
.Replace("\r\n", "\n")
.Replace("\n", Environment.NewLine);
}

public static string TrimNewLines(this string value)
{
return value.TrimEnd('\r', '\n');
}
}
}
8 changes: 6 additions & 2 deletions YamlDotNet.Test/Yaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ public static Scanner ScannerForText(string yamlText)
}

public static StringReader ReaderForText(string yamlText)
{
return new StringReader(Text(yamlText));
}

public static string Text(string yamlText)
{
var lines = yamlText
.Split('\n')
Expand All @@ -102,8 +107,7 @@ public static StringReader ReaderForText(string yamlText)
.ToList();
}

var reader = new StringReader(string.Join("\n", lines.ToArray()));
return reader;
return string.Join("\n", lines.ToArray());
}
}
}
1 change: 1 addition & 0 deletions YamlDotNet.Test/YamlDotNet.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="Microsoft.TestPlatform.TestHost" Version="15.0.0" />
<PackageReference Include="System.Drawing.Primitives" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta1-build3642" />
Expand Down
10 changes: 10 additions & 0 deletions YamlDotNet/Helpers/Portability.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@ public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type)
: type.GetRuntimeProperties().Where(instancePublic);
}

public static IEnumerable<FieldInfo> GetPublicFields(this Type type)
{
return type.GetRuntimeFields().Where(f => !f.IsStatic && f.IsPublic);
}

public static IEnumerable<MethodInfo> GetPublicStaticMethods(this Type type)
{
return type.GetRuntimeMethods()
Expand Down Expand Up @@ -327,6 +332,11 @@ public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type)
: type.GetProperties(instancePublic);
}

public static IEnumerable<FieldInfo> GetPublicFields(this Type type)
{
return type.GetFields(BindingFlags.Instance | BindingFlags.Public);
}

public static IEnumerable<MethodInfo> GetPublicStaticMethods(this Type type)
{
return type.GetMethods(BindingFlags.Static | BindingFlags.Public);
Expand Down
24 changes: 21 additions & 3 deletions YamlDotNet/Serialization/BuilderSkeleton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public abstract class BuilderSkeleton<TBuilder>
internal readonly YamlAttributeOverrides overrides;
internal readonly LazyComponentRegistrationList<Nothing, IYamlTypeConverter> typeConverterFactories;
internal readonly LazyComponentRegistrationList<ITypeInspector, ITypeInspector> typeInspectorFactories;
private bool ignoreFields;

internal BuilderSkeleton()
{
Expand All @@ -54,9 +55,26 @@ internal BuilderSkeleton()

internal ITypeInspector BuildTypeInspector()
{
return typeInspectorFactories.BuildComponentChain(
new ReadablePropertiesTypeInspector(typeResolver)
);
ITypeInspector innerInspector = new ReadablePropertiesTypeInspector(typeResolver);
if (!ignoreFields)
{
innerInspector = new CompositeTypeInspector(
new ReadableFieldsTypeInspector(typeResolver),
innerInspector
);
}

return typeInspectorFactories.BuildComponentChain(innerInspector);
}

/// <summary>
/// Prevents serialization and deserialization of fields.
/// </summary>
/// <returns></returns>
public TBuilder IgnoreFields()
{
ignoreFields = true;
return Self;
}

/// <summary>
Expand Down
51 changes: 51 additions & 0 deletions YamlDotNet/Serialization/TypeInspectors/CompositeTypeInspector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;

namespace YamlDotNet.Serialization.TypeInspectors
{
/// <summary>
/// Aggregates the results from multiple <see cref="ITypeInspector" /> into a single one.
/// </summary>
public sealed class CompositeTypeInspector : TypeInspectorSkeleton
{
private readonly IEnumerable<ITypeInspector> typeInspectors;

public CompositeTypeInspector(params ITypeInspector[] typeInspectors)
: this((IEnumerable<ITypeInspector>)typeInspectors)
{
}

public CompositeTypeInspector(IEnumerable<ITypeInspector> typeInspectors)
{
this.typeInspectors = typeInspectors?.ToList() ?? throw new ArgumentNullException(nameof(typeInspectors));
}

public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
{
return typeInspectors
.SelectMany(i => i.GetProperties(type, container));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using YamlDotNet.Core;

namespace YamlDotNet.Serialization.TypeInspectors
{
/// <summary>
/// Returns the properties and fields of a type that are readable.
/// </summary>
public sealed class ReadableFieldsTypeInspector : TypeInspectorSkeleton
{
private readonly ITypeResolver typeResolver;

public ReadableFieldsTypeInspector(ITypeResolver typeResolver)
{
this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver");
}

public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
{
return type
.GetPublicFields()
.Select(p => (IPropertyDescriptor)new ReflectionFieldDescriptor(p, typeResolver));
}

private sealed class ReflectionFieldDescriptor : IPropertyDescriptor
{
private readonly FieldInfo fieldInfo;
private readonly ITypeResolver typeResolver;

public ReflectionFieldDescriptor(FieldInfo fieldInfo, ITypeResolver typeResolver)
{
this.fieldInfo = fieldInfo;
this.typeResolver = typeResolver;
ScalarStyle = ScalarStyle.Any;
}

public string Name { get { return fieldInfo.Name; } }
public Type Type { get { return fieldInfo.FieldType; } }
public Type TypeOverride { get; set; }
public int Order { get; set; }
public bool CanWrite { get { return !fieldInfo.IsInitOnly; } }
public ScalarStyle ScalarStyle { get; set; }

public void Write(object target, object value)
{
fieldInfo.SetValue(target, value);
}

public T GetCustomAttribute<T>() where T : Attribute
{
var attributes = fieldInfo.GetCustomAttributes(typeof(T), true);
return (T)attributes.FirstOrDefault();
}

public IObjectDescriptor Read(object target)
{
var propertyValue = fieldInfo.GetValue(target);
var actualType = TypeOverride ?? typeResolver.Resolve(Type, propertyValue);
return new ObjectDescriptor(propertyValue, actualType, Type, ScalarStyle);
}
}
}
}

0 comments on commit b542ab9

Please sign in to comment.