Skip to content

Commit 9a5900f

Browse files
authored
Merge pull request #774 from JJ11teen/buffered-deserialisation
Buffered deserialisation +semver:feature
2 parents f460b5c + 4efa76c commit 9a5900f

13 files changed

+1130
-0
lines changed
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
// This file is part of YamlDotNet - A .NET library for YAML.
2+
// Copyright (c) Antoine Aubry and contributors
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
5+
// this software and associated documentation files (the "Software"), to deal in
6+
// the Software without restriction, including without limitation the rights to
7+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8+
// of the Software, and to permit persons to whom the Software is furnished to do
9+
// so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in all
12+
// copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
// SOFTWARE.
21+
22+
using System;
23+
using System.Collections.Generic;
24+
using System.Linq;
25+
using FluentAssertions;
26+
using Xunit;
27+
using YamlDotNet.Core;
28+
using YamlDotNet.Serialization;
29+
using YamlDotNet.Serialization.NamingConventions;
30+
31+
namespace YamlDotNet.Test.Serialization.BufferedDeserialization
32+
{
33+
public class KeyValueTypeDiscriminatorTests
34+
{
35+
[Fact]
36+
public void KeyValueTypeDiscriminator_WithParentBaseType_Single()
37+
{
38+
var bufferedDeserializer = new DeserializerBuilder()
39+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
40+
.WithTypeDiscriminatingNodeDeserializer(options => {
41+
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
42+
"kind",
43+
new Dictionary<string, Type>()
44+
{
45+
{ "Namespace", typeof(KubernetesNamespace) },
46+
{ "Service", typeof(KubernetesService) }
47+
});
48+
},
49+
maxDepth: 3,
50+
maxLength: 40)
51+
.Build();
52+
53+
var service = bufferedDeserializer.Deserialize<KubernetesResource>(KubernetesServiceYaml);
54+
service.Should().BeOfType<KubernetesService>();
55+
}
56+
57+
[Fact]
58+
public void KeyValueTypeDiscriminator_WithParentBaseType_List()
59+
{
60+
var bufferedDeserializer = new DeserializerBuilder()
61+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
62+
.WithTypeDiscriminatingNodeDeserializer(options => {
63+
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
64+
"kind",
65+
new Dictionary<string, Type>()
66+
{
67+
{ "Namespace", typeof(KubernetesNamespace) },
68+
{ "Service", typeof(KubernetesService) }
69+
});
70+
},
71+
maxDepth: 3,
72+
maxLength: 40)
73+
.Build();
74+
75+
var resources = bufferedDeserializer.Deserialize<List<KubernetesResource>>(ListOfKubernetesYaml);
76+
resources[0].Should().BeOfType<KubernetesNamespace>();
77+
resources[1].Should().BeOfType<KubernetesService>();
78+
}
79+
80+
[Fact]
81+
public void KeyValueTypeDiscriminator_WithObjectBaseType_Single()
82+
{
83+
var bufferedDeserializer = new DeserializerBuilder()
84+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
85+
.WithTypeDiscriminatingNodeDeserializer(options => {
86+
options.AddKeyValueTypeDiscriminator<object>(
87+
"kind",
88+
new Dictionary<string, Type>()
89+
{
90+
{ "Namespace", typeof(KubernetesNamespace) },
91+
{ "Service", typeof(KubernetesService) }
92+
});
93+
},
94+
maxDepth: 3,
95+
maxLength: 40)
96+
.Build();
97+
98+
var service = bufferedDeserializer.Deserialize<object>(KubernetesServiceYaml);
99+
service.Should().BeOfType<KubernetesService>();
100+
}
101+
102+
[Fact]
103+
public void KeyValueTypeDiscriminator_WithObjectBaseType_List()
104+
{
105+
var bufferedDeserializer = new DeserializerBuilder()
106+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
107+
.WithTypeDiscriminatingNodeDeserializer(options => {
108+
options.AddKeyValueTypeDiscriminator<object>(
109+
"kind",
110+
new Dictionary<string, Type>()
111+
{
112+
{ "Namespace", typeof(KubernetesNamespace) },
113+
{ "Service", typeof(KubernetesService) }
114+
});
115+
},
116+
maxDepth: 3,
117+
maxLength: 30)
118+
.Build();
119+
120+
var resources = bufferedDeserializer.Deserialize<List<object>>(ListOfKubernetesYaml);
121+
resources[0].Should().BeOfType<KubernetesNamespace>();
122+
resources[1].Should().BeOfType<KubernetesService>();
123+
}
124+
125+
[Fact]
126+
public void KeyValueTypeDiscriminator_WithInterfaceBaseType_Single()
127+
{
128+
var bufferedDeserializer = new DeserializerBuilder()
129+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
130+
.WithTypeDiscriminatingNodeDeserializer(options => {
131+
options.AddKeyValueTypeDiscriminator<IKubernetesResource>(
132+
"kind",
133+
new Dictionary<string, Type>()
134+
{
135+
{ "Namespace", typeof(KubernetesNamespace) },
136+
{ "Service", typeof(KubernetesService) }
137+
});
138+
},
139+
maxDepth: 3,
140+
maxLength: 40)
141+
.Build();
142+
143+
var service = bufferedDeserializer.Deserialize<IKubernetesResource>(KubernetesServiceYaml);
144+
service.Should().BeOfType<KubernetesService>();
145+
}
146+
147+
[Fact]
148+
public void KeyValueTypeDiscriminator_WithInterfaceBaseType_List()
149+
{
150+
var bufferedDeserializer = new DeserializerBuilder()
151+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
152+
.WithTypeDiscriminatingNodeDeserializer(options => {
153+
options.AddKeyValueTypeDiscriminator<IKubernetesResource>(
154+
"kind",
155+
new Dictionary<string, Type>()
156+
{
157+
{ "Namespace", typeof(KubernetesNamespace) },
158+
{ "Service", typeof(KubernetesService) }
159+
});
160+
},
161+
maxDepth: 3,
162+
maxLength: 30)
163+
.Build();
164+
165+
var resources = bufferedDeserializer.Deserialize<List<IKubernetesResource>>(ListOfKubernetesYaml);
166+
resources[0].Should().BeOfType<KubernetesNamespace>();
167+
resources[1].Should().BeOfType<KubernetesService>();
168+
}
169+
170+
[Fact]
171+
public void KeyValueTypeDiscriminator_MultipleWithSameKey()
172+
{
173+
var bufferedDeserializer = new DeserializerBuilder()
174+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
175+
.WithTypeDiscriminatingNodeDeserializer(options => {
176+
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
177+
"kind",
178+
new Dictionary<string, Type>()
179+
{
180+
{ "Namespace", typeof(KubernetesNamespace) },
181+
});
182+
options.AddKeyValueTypeDiscriminator<KubernetesResource>(
183+
"kind",
184+
new Dictionary<string, Type>()
185+
{
186+
{ "Service", typeof(KubernetesService) }
187+
});
188+
},
189+
maxDepth: 3,
190+
maxLength: 40)
191+
.Build();
192+
193+
var resources = bufferedDeserializer.Deserialize<List<KubernetesResource>>(ListOfKubernetesYaml);
194+
resources[0].Should().BeOfType<KubernetesNamespace>();
195+
resources[1].Should().BeOfType<KubernetesService>();
196+
}
197+
198+
public const string ListOfKubernetesYaml = @"
199+
- apiVersion: v1
200+
kind: Namespace
201+
metadata:
202+
name: test-namespace
203+
- apiVersion: v1
204+
kind: Service
205+
metadata:
206+
name: my-service
207+
spec:
208+
selector:
209+
app.kubernetes.io/name: MyApp
210+
ports:
211+
- protocol: TCP
212+
port: 80
213+
targetPort: 9376
214+
";
215+
216+
public const string KubernetesServiceYaml = @"
217+
apiVersion: v1
218+
kind: Service
219+
metadata:
220+
name: my-service
221+
spec:
222+
selector:
223+
app.kubernetes.io/name: MyApp
224+
ports:
225+
- protocol: TCP
226+
port: 80
227+
targetPort: 9376
228+
";
229+
230+
public interface IKubernetesResource { }
231+
232+
public class KubernetesResource : IKubernetesResource
233+
{
234+
public string ApiVersion { get; set; }
235+
public string Kind { get; set; }
236+
public KubernetesMetadata Metadata { get; set; }
237+
238+
public class KubernetesMetadata
239+
{
240+
public string Name { get; set; }
241+
}
242+
}
243+
244+
public class KubernetesService : KubernetesResource
245+
{
246+
public KubernetesServiceSpec Spec { get; set; }
247+
public class KubernetesServiceSpec
248+
{
249+
public Dictionary<string, string> Selector { get; set; }
250+
public List<KubernetesServicePort> Ports { get; set; }
251+
public class KubernetesServicePort
252+
{
253+
public string Protocol { get; set; }
254+
public int Port { get; set; }
255+
public int TargetPort { get; set; }
256+
}
257+
}
258+
}
259+
260+
public class KubernetesNamespace : KubernetesResource
261+
{
262+
263+
}
264+
}
265+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// This file is part of YamlDotNet - A .NET library for YAML.
2+
// Copyright (c) Antoine Aubry and contributors
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
5+
// this software and associated documentation files (the "Software"), to deal in
6+
// the Software without restriction, including without limitation the rights to
7+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8+
// of the Software, and to permit persons to whom the Software is furnished to do
9+
// so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in all
12+
// copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
// SOFTWARE.
21+
22+
using System;
23+
using System.Collections.Generic;
24+
using System.Linq;
25+
using FluentAssertions;
26+
using Xunit;
27+
using YamlDotNet.Core;
28+
using YamlDotNet.Serialization;
29+
using YamlDotNet.Serialization.NamingConventions;
30+
31+
namespace YamlDotNet.Test.Serialization.BufferedDeserialization
32+
{
33+
public class TypeDiscriminatingNodeDeserializerTests
34+
{
35+
[Fact]
36+
public void TypeDiscriminatingNodeDeserializer_ThrowsWhen_MaxDepthExceeded()
37+
{
38+
var bufferedDeserializer = new DeserializerBuilder()
39+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
40+
.WithTypeDiscriminatingNodeDeserializer(options => {
41+
options.AddKeyValueTypeDiscriminator<object>("kind", new Dictionary<string, Type>());
42+
},
43+
maxDepth: 2,
44+
maxLength: 40)
45+
.Build();
46+
47+
Action act = () => bufferedDeserializer.Deserialize<object>(KubernetesServiceYaml);
48+
act
49+
.ShouldThrow<YamlException>()
50+
.WithMessage("Failed to buffer yaml node")
51+
.WithInnerException<ArgumentOutOfRangeException>()
52+
.Where(e => e.InnerException.Message.Contains("Parser buffer exceeded max depth"));
53+
}
54+
55+
[Fact]
56+
public void TypeDiscriminatingNodeDeserializer_ThrowsWhen_MaxLengthExceeded()
57+
{
58+
var bufferedDeserializer = new DeserializerBuilder()
59+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
60+
.WithTypeDiscriminatingNodeDeserializer(options => {
61+
options.AddKeyValueTypeDiscriminator<object>("kind", new Dictionary<string, Type>());
62+
},
63+
maxDepth: 3,
64+
maxLength: 20)
65+
.Build();
66+
67+
Action act = () => bufferedDeserializer.Deserialize<object>(KubernetesServiceYaml);
68+
act
69+
.ShouldThrow<YamlException>()
70+
.WithMessage("Failed to buffer yaml node")
71+
.WithInnerException<ArgumentOutOfRangeException>()
72+
.Where(e => e.InnerException.Message.Contains("Parser buffer exceeded max length"));
73+
}
74+
75+
public const string KubernetesServiceYaml = @"
76+
apiVersion: v1
77+
kind: Service
78+
metadata:
79+
name: my-service
80+
spec:
81+
selector:
82+
app.kubernetes.io/name: MyApp
83+
ports:
84+
- protocol: TCP
85+
port: 80
86+
targetPort: 9376
87+
";
88+
}
89+
}

0 commit comments

Comments
 (0)