Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffered deserialisation #774

Merged
merged 32 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b3fbc1f
add BufferedDeserialization
JJ11teen Feb 2, 2023
b09200c
improve some comments
JJ11teen Feb 2, 2023
b3d65b0
pr design changes
JJ11teen Feb 3, 2023
8dea96d
make naming consistent
JJ11teen Feb 3, 2023
cc6bc82
more naming fixes
JJ11teen Feb 3, 2023
803633e
fix assignment direction, allow fall through when no matches
JJ11teen Feb 3, 2023
4b40917
add first test
JJ11teen Feb 3, 2023
bfb6669
use all deserializers as inner deserializers
JJ11teen Feb 6, 2023
a9b28e9
extend tests
JJ11teen Feb 6, 2023
280d488
rename tests
JJ11teen Feb 6, 2023
91e9e63
add buffer limit tests
JJ11teen Feb 6, 2023
52e600a
invert to IsAssignableFrom
JJ11teen Feb 6, 2023
5a5e4f7
remove default interface imp when < net7
JJ11teen Feb 6, 2023
ec91165
changes to support older dotnet versions
JJ11teen Feb 7, 2023
57fe875
make exception message tests dotnet version independent
JJ11teen Feb 7, 2023
12e6b59
add public comments
JJ11teen Feb 7, 2023
3b9b839
add class doc for BufferedNodeDeserializer
JJ11teen Feb 7, 2023
b7111c7
add UniqueKeyTypeDiscriminatorTests
JJ11teen Feb 7, 2023
410d9cc
fix attribute formatting
JJ11teen Feb 7, 2023
fdae9e4
add method to static builder
JJ11teen Feb 8, 2023
9a99c62
Add comments to TryFindMappingEntry, move null check to start
JJ11teen Mar 5, 2023
0116037
add xml comments to ParserBuffer
JJ11teen Mar 5, 2023
48ad5a8
add xml comments for IValueTypeDiscriminator
JJ11teen Mar 5, 2023
db00544
fail discrimination more softly, add xml comments
JJ11teen Mar 5, 2023
4693d3d
add xml comments for uniqueKey
JJ11teen Mar 5, 2023
9cc5285
improve comment
JJ11teen Mar 5, 2023
18a309a
improve exception
JJ11teen Mar 5, 2023
5a01713
improve comments
JJ11teen Mar 10, 2023
13919ac
rename to TypeDiscriminatingNodeDeserializer
JJ11teen Mar 10, 2023
1c5e8a7
add new test
JJ11teen Mar 10, 2023
883cd73
typos & comment improvements
JJ11teen Mar 18, 2023
4efa76c
add copy of license to each file
JJ11teen Mar 18, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
// 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;
JJ11teen marked this conversation as resolved.
Show resolved Hide resolved
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

namespace YamlDotNet.Test.Serialization.BufferedDeserialization
{
public class KeyValueTypeDiscriminatorTests
{
[Fact]
public void KeyValueTypeDiscriminator_WithParentBaseType_Single()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
"kind",
new Dictionary<string, Type>()
{
{ "Namespace", typeof(KubernetesNamespace) },
{ "Service", typeof(KubernetesService) }
});
},
maxDepth: 3,
maxLength: 40)
.Build();

var service = bufferedDeserializer.Deserialize<KubernetesResource>(KubernetesServiceYaml);
service.Should().BeOfType<KubernetesService>();
}

[Fact]
public void KeyValueTypeDiscriminator_WithParentBaseType_List()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
"kind",
new Dictionary<string, Type>()
{
{ "Namespace", typeof(KubernetesNamespace) },
{ "Service", typeof(KubernetesService) }
});
},
maxDepth: 3,
maxLength: 40)
.Build();

var resources = bufferedDeserializer.Deserialize<List<KubernetesResource>>(ListOfKubernetesYaml);
resources[0].Should().BeOfType<KubernetesNamespace>();
resources[1].Should().BeOfType<KubernetesService>();
}

[Fact]
public void KeyValueTypeDiscriminator_WithObjectBaseType_Single()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<object>(
"kind",
new Dictionary<string, Type>()
{
{ "Namespace", typeof(KubernetesNamespace) },
{ "Service", typeof(KubernetesService) }
});
},
maxDepth: 3,
maxLength: 40)
.Build();

var service = bufferedDeserializer.Deserialize<object>(KubernetesServiceYaml);
service.Should().BeOfType<KubernetesService>();
}

[Fact]
public void KeyValueTypeDiscriminator_WithObjectBaseType_List()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<object>(
"kind",
new Dictionary<string, Type>()
{
{ "Namespace", typeof(KubernetesNamespace) },
{ "Service", typeof(KubernetesService) }
});
},
maxDepth: 3,
maxLength: 30)
.Build();

var resources = bufferedDeserializer.Deserialize<List<object>>(ListOfKubernetesYaml);
resources[0].Should().BeOfType<KubernetesNamespace>();
resources[1].Should().BeOfType<KubernetesService>();
}

[Fact]
public void KeyValueTypeDiscriminator_WithInterfaceBaseType_Single()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<IKubernetesResource>(
"kind",
new Dictionary<string, Type>()
{
{ "Namespace", typeof(KubernetesNamespace) },
{ "Service", typeof(KubernetesService) }
});
},
maxDepth: 3,
maxLength: 40)
.Build();

var service = bufferedDeserializer.Deserialize<IKubernetesResource>(KubernetesServiceYaml);
service.Should().BeOfType<KubernetesService>();
}

[Fact]
public void KeyValueTypeDiscriminator_WithInterfaceBaseType_List()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<IKubernetesResource>(
"kind",
new Dictionary<string, Type>()
{
{ "Namespace", typeof(KubernetesNamespace) },
{ "Service", typeof(KubernetesService) }
});
},
maxDepth: 3,
maxLength: 30)
.Build();

var resources = bufferedDeserializer.Deserialize<List<IKubernetesResource>>(ListOfKubernetesYaml);
resources[0].Should().BeOfType<KubernetesNamespace>();
resources[1].Should().BeOfType<KubernetesService>();
}

[Fact]
public void KeyValueTypeDiscriminator_MultipleWithSameKey()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
"kind",
new Dictionary<string, Type>()
{
{ "Namespace", typeof(KubernetesNamespace) },
});
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
"kind",
new Dictionary<string, Type>()
{
{ "Service", typeof(KubernetesService) }
});
},
maxDepth: 3,
maxLength: 40)
.Build();

var resources = bufferedDeserializer.Deserialize<List<KubernetesResource>>(ListOfKubernetesYaml);
resources[0].Should().BeOfType<KubernetesNamespace>();
resources[1].Should().BeOfType<KubernetesService>();
}

public const string ListOfKubernetesYaml = @"
- apiVersion: v1
kind: Namespace
metadata:
name: test-namespace
- apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
";

public const string KubernetesServiceYaml = @"
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
";

public interface IKubernetesResource { }

public class KubernetesResource : IKubernetesResource
{
public string ApiVersion { get; set; }
public string Kind { get; set; }
public KubernetesMetadata Metadata { get; set; }

public class KubernetesMetadata
{
public string Name { get; set; }
}
}

public class KubernetesService : KubernetesResource
{
public KubernetesServiceSpec Spec { get; set; }
public class KubernetesServiceSpec
{
public Dictionary<string, string> Selector { get; set; }
public List<KubernetesServicePort> Ports { get; set; }
public class KubernetesServicePort
{
public string Protocol { get; set; }
public int Port { get; set; }
public int TargetPort { get; set; }
}
}
}

public class KubernetesNamespace : KubernetesResource
{

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// 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;
JJ11teen marked this conversation as resolved.
Show resolved Hide resolved
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

namespace YamlDotNet.Test.Serialization.BufferedDeserialization
{
public class TypeDiscriminatingNodeDeserializerTests
{
[Fact]
public void TypeDiscriminatingNodeDeserializer_ThrowsWhen_MaxDepthExceeded()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<object>("kind", new Dictionary<string, Type>());
},
maxDepth: 2,
maxLength: 40)
.Build();

Action act = () => bufferedDeserializer.Deserialize<object>(KubernetesServiceYaml);
act
.ShouldThrow<YamlException>()
.WithMessage("Failed to buffer yaml node")
.WithInnerException<ArgumentOutOfRangeException>()
.Where(e => e.InnerException.Message.Contains("Parser buffer exceeded max depth"));
}

[Fact]
public void TypeDiscriminatingNodeDeserializer_ThrowsWhen_MaxLengthExceeded()
{
var bufferedDeserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithTypeDiscriminatingNodeDeserializer(options => {
options.AddKeyValueTypeDiscriminator<object>("kind", new Dictionary<string, Type>());
},
maxDepth: 3,
maxLength: 20)
.Build();

Action act = () => bufferedDeserializer.Deserialize<object>(KubernetesServiceYaml);
act
.ShouldThrow<YamlException>()
.WithMessage("Failed to buffer yaml node")
.WithInnerException<ArgumentOutOfRangeException>()
.Where(e => e.InnerException.Message.Contains("Parser buffer exceeded max length"));
}

public const string KubernetesServiceYaml = @"
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
";
}
}
Loading