Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion YamlDotNet.Test/Serialization/DeserializerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public void UnquotedStringTypeDeserialization_RegularNumbers(object expected)

var yaml = FormattableString.Invariant($"Value: {expected}");

// It needs explicitly specifying maximum precision for value roundtrip.
// It needs explicitly specifying maximum precision for value roundtrip.
if (expected is float floatValue)
{
yaml = $"Value: {floatValue.ToString("G9", CultureInfo.InvariantCulture)}";
Expand Down Expand Up @@ -696,6 +696,92 @@ public enum EnumMemberedEnum
}
#endif

#if NET6_0_OR_GREATER
[Fact]
public void RecordClassWithPositionalProperties()
{
var deserializer = new DeserializerBuilder()
.EnableUnstableAttemptUninitializedInstance()
.Build();
var yaml = @"
Name: Jack
Age: 43";
var actual = deserializer.Deserialize<PersonRecordClass>(yaml);
Assert.Equal("Jack", actual.Name);
Assert.Equal(43, actual.Age);
}

[Fact]
public void RecordStructWithPositionalProperties()
{
var deserializer = new DeserializerBuilder()
.EnableUnstableAttemptUninitializedInstance()
.Build();
var yaml = @"
Name: Jack
Age: 43";
var actual = deserializer.Deserialize<PersonRecordStruct>(yaml);
Assert.Equal("Jack", actual.Name);
Assert.Equal(43, actual.Age);
}

[Fact]
public void RecordClassWithPositionalPropertiesWithNamingConvention()
{
var deserializer = new DeserializerBuilder()
.EnableUnstableAttemptUninitializedInstance()
.WithNamingConvention(LowerCaseNamingConvention.Instance)
.Build();
var yaml = @"
name: Jack
age: 43";
var actual = deserializer.Deserialize<PersonRecordClass>(yaml);
Assert.Equal("Jack", actual.Name);
Assert.Equal(43, actual.Age);
}

[Fact]
public void RecordStructWithPositionalPropertiesWithNamingConvention()
{
var deserializer = new DeserializerBuilder()
.EnableUnstableAttemptUninitializedInstance()
.WithNamingConvention(LowerCaseNamingConvention.Instance)
.Build();
var yaml = @"
name: Jack
age: 43";
var actual = deserializer.Deserialize<PersonRecordStruct>(yaml);
Assert.Equal("Jack", actual.Name);
Assert.Equal(43, actual.Age);
}

[Fact]
public void NestedRecordClassWithPositionalPropertiesWithNamingConvention()
{
var deserializer = new DeserializerBuilder()
.EnableUnstableAttemptUninitializedInstance()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.Build();
var yaml = @"
name: Jack
year_of_birth: 1983
car:
name: Mercedes
year: 2018";
var actual = deserializer.Deserialize<PersonRecordWithCarRecord>(yaml);
Assert.Equal("Jack", actual.Name);
Assert.Equal(1983, actual.YearOfBirth);
Assert.Equal("Mercedes", actual.Car.Name);
Assert.Equal(2018, actual.Car.Year);
}

public record class PersonRecordClass(string Name, int Age);
public record struct PersonRecordStruct(string Name, int Age);

public record PersonRecordWithCarRecord(string Name, int YearOfBirth, CarRecord Car);
public record CarRecord(string Name, int Year);
#endif

#nullable enable
public class NullableDefaultClass
{
Expand Down
10 changes: 10 additions & 0 deletions YamlDotNet/Serialization/BuilderSkeleton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ public TBuilder EnablePrivateConstructors()
return Self;
}

/// <summary>
/// Calling this will attempt to create an uninitialized instance during deserialization, possibly allowing certain types.
/// </summary>
/// <remarks>This setting is UNSTABLE.</remarks>
public TBuilder EnableUnstableAttemptUninitializedInstance()
{
settings.UnstableAttemptUninitializedInstance = true;
return Self;
}

/// <summary>
/// Sets the <see cref="INamingConvention" /> that will be used by the (de)serializer.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions YamlDotNet/Serialization/ObjectFactories/DefaultObjectFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using RuntimeSerialization = System.Runtime.Serialization;
using YamlDotNet.Serialization.Callbacks;

namespace YamlDotNet.Serialization.ObjectFactories
Expand Down Expand Up @@ -109,6 +111,14 @@ public override object Create(Type type)
{
return Activator.CreateInstance(type, settings.AllowPrivateConstructors)!;
}
catch (MissingMethodException) when (settings.UnstableAttemptUninitializedInstance)
{
#if NET5_0_OR_GREATER
return RuntimeHelpers.GetUninitializedObject(type);
#else
return RuntimeSerialization.FormatterServices.GetUninitializedObject(type);
#endif
}
catch (Exception err)
{
var message = $"Failed to create an instance of type '{type.FullName}'.";
Expand Down
5 changes: 5 additions & 0 deletions YamlDotNet/Serialization/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ public class Settings
/// If true then private, parameterless constructors will be invoked if a public one is not available.
/// </summary>
public bool AllowPrivateConstructors { get; set; }

/// <summary>
/// If true then will attempt to create an uninitialized instance during deserialization, possibly allowing certain type, such as <c>record</c>s with positional properties.
/// </summary>
public bool UnstableAttemptUninitializedInstance { get; set; }
}
}