Skip to content

Commit 71084f7

Browse files
authored
Merge pull request #11 from jamesshenry/fixes
feat: Allow non scalar list elements to be serialized with simple collection node names
2 parents 769dce0 + 742fec0 commit 71084f7

4 files changed

Lines changed: 129 additions & 8 deletions

File tree

src/Kuddle.Net.Tests/Serialization/ObjectMapperTests.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,104 @@ public async Task Deserialize_FlattenedCollection_CollectsAllMatchingNodes()
701701
await Assert.That(result.FlattenedServers[1].Host).IsEqualTo("remote");
702702
}
703703

704+
[Test]
705+
public async Task Serialize_WithSimpleCollectionNodeNames_True_UseDashNodeNames()
706+
{
707+
// Arrange
708+
var model = new CollectionModel
709+
{
710+
WrappedPlugins = [new() { Name = "Auth" }],
711+
FlattenedServers = [new() { Host = "localhost" }],
712+
};
713+
714+
var options = new KdlSerializerOptions { SimpleCollectionNodeNames = true };
715+
716+
// Act
717+
var kdl = KdlSerializer.Serialize(model, options);
718+
719+
// Assert - should use dash (-) for collection items
720+
await Assert.That(kdl).Contains("plugins {");
721+
await Assert.That(kdl).Contains("- Auth");
722+
}
723+
724+
[Test]
725+
public async Task Serialize_WithSimpleCollectionNodeNames_False_UseTypedNodeNames()
726+
{
727+
// Arrange
728+
var model = new CollectionModel
729+
{
730+
WrappedPlugins = [new() { Name = "Auth" }],
731+
FlattenedServers = [new() { Host = "localhost" }],
732+
};
733+
734+
var options = new KdlSerializerOptions { SimpleCollectionNodeNames = false };
735+
736+
// Act
737+
var kdl = KdlSerializer.Serialize(model, options);
738+
739+
// Assert - should use typed node names
740+
await Assert.That(kdl).Contains("plugins {");
741+
await Assert.That(kdl).Contains("plugin-info Auth");
742+
}
743+
744+
[Test]
745+
public async Task RoundTrip_SerializeAndDeserialize_WithSimpleCollectionNodeNames_True()
746+
{
747+
// Arrange
748+
var originalModel = new CollectionModel
749+
{
750+
WrappedPlugins = [new() { Name = "Auth" }, new() { Name = "Logging" }],
751+
FlattenedServers = [new() { Host = "localhost" }, new() { Host = "remote" }],
752+
};
753+
754+
var options = new KdlSerializerOptions
755+
{
756+
SimpleCollectionNodeNames = true,
757+
UnwrapRoot = true,
758+
};
759+
760+
// Act
761+
var kdl = KdlSerializer.Serialize(originalModel, options);
762+
var deserializedModel = KdlSerializer.Deserialize<CollectionModel>(kdl, options);
763+
764+
// Assert
765+
await Assert.That(deserializedModel.WrappedPlugins).Count().IsEqualTo(2);
766+
await Assert.That(deserializedModel.WrappedPlugins[0].Name).IsEqualTo("Auth");
767+
await Assert.That(deserializedModel.WrappedPlugins[1].Name).IsEqualTo("Logging");
768+
await Assert.That(deserializedModel.FlattenedServers).Count().IsEqualTo(2);
769+
await Assert.That(deserializedModel.FlattenedServers[0].Host).IsEqualTo("localhost");
770+
await Assert.That(deserializedModel.FlattenedServers[1].Host).IsEqualTo("remote");
771+
}
772+
773+
[Test]
774+
public async Task RoundTrip_SerializeAndDeserialize_WithSimpleCollectionNodeNames_False()
775+
{
776+
// Arrange
777+
var originalModel = new CollectionModel
778+
{
779+
WrappedPlugins = [new() { Name = "Auth" }, new() { Name = "Logging" }],
780+
FlattenedServers = [new() { Host = "localhost" }, new() { Host = "remote" }],
781+
};
782+
783+
var options = new KdlSerializerOptions
784+
{
785+
SimpleCollectionNodeNames = false,
786+
UnwrapRoot = false,
787+
};
788+
789+
// Act
790+
var kdl = KdlSerializer.Serialize(originalModel, options);
791+
var deserializedModel = KdlSerializer.Deserialize<CollectionModel>(kdl, options);
792+
793+
// Assert
794+
await Assert.That(deserializedModel.WrappedPlugins).Count().IsEqualTo(2);
795+
await Assert.That(deserializedModel.WrappedPlugins[0].Name).IsEqualTo("Auth");
796+
await Assert.That(deserializedModel.WrappedPlugins[1].Name).IsEqualTo("Logging");
797+
await Assert.That(deserializedModel.FlattenedServers).Count().IsEqualTo(2);
798+
await Assert.That(deserializedModel.FlattenedServers[0].Host).IsEqualTo("localhost");
799+
await Assert.That(deserializedModel.FlattenedServers[1].Host).IsEqualTo("remote");
800+
}
801+
704802
#endregion
705803

706804
#region Test Models

src/Kuddle.Net/Serialization/KdlSerializerOptions.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ namespace Kuddle.Serialization;
55
/// </summary>
66
public record KdlSerializerOptions
77
{
8+
/// <summary>
9+
/// Default options instance.
10+
/// </summary>
11+
public static KdlSerializerOptions Default { get; } = new();
12+
813
/// <summary>
914
/// Whether to ignore null values when serializing. Default is true.
1015
/// </summary>
@@ -21,8 +26,8 @@ public record KdlSerializerOptions
2126
public bool WriteTypeAnnotations { get; init; } = true;
2227

2328
/// <summary>
24-
/// Default options instance.
29+
/// Whether to use simple dash (-) for collection item node names instead of the mapped type name. Default is true.
2530
/// </summary>
26-
public static KdlSerializerOptions Default { get; } = new();
31+
public bool SimpleCollectionNodeNames { get; init; } = true;
2732
public bool UnwrapRoot { get; init; } = false;
2833
}

src/Kuddle.Net/Serialization/ObjectDeserializer.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ internal static T DeserializeDocument<T>(KdlDocument doc, KdlSerializerOptions?
2525

2626
var mapping = KdlTypeMapping.For<T>();
2727
var instance = new T();
28+
if (options?.UnwrapRoot == false)
29+
{
30+
if (doc.Nodes.Count != 1)
31+
{
32+
throw new KuddleSerializationException(
33+
$"Expected exactly 1 root node, but found {doc.Nodes.Count}."
34+
);
35+
}
36+
worker.MapNodeToObject(doc.Nodes.First(), instance, mapping);
37+
return instance;
38+
}
2839

2940
if (mapping.Arguments.Count > 0 || mapping.Properties.Count > 0)
3041
{

src/Kuddle.Net/Serialization/ObjectSerializer.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ internal static KdlDocument SerializeDocument<T>(T? instance, KdlSerializerOptio
6060
// Map positional arguments to anonymous nodes
6161
doc.Nodes.Add(new KdlNode(KdlValue.From("-")) { Entries = [arg] });
6262
}
63-
if (rootNode.Children != null)
64-
{
65-
doc.Nodes.AddRange(rootNode.Children.Nodes);
66-
}
63+
}
64+
if (rootNode.Children != null)
65+
{
66+
doc.Nodes.AddRange(rootNode.Children.Nodes);
6767
}
6868
}
6969
else
@@ -198,8 +198,15 @@ private IEnumerable<KdlNode> SerializeCollection(IEnumerable enumerable, KdlMemb
198198
}
199199
}
200200

201-
private static string GetDefaultNodeName(object item) =>
202-
item.GetType().IsKdlScalar ? "-" : KdlTypeMapping.For(item.GetType()).NodeName;
201+
private string GetDefaultNodeName(object item)
202+
{
203+
if (item.GetType().IsKdlScalar)
204+
return "-";
205+
206+
return _options.SimpleCollectionNodeNames
207+
? "-"
208+
: KdlTypeMapping.For(item.GetType()).NodeName;
209+
}
203210

204211
private KdlNode MapToNode(object item, string kdlName, string? typeAnnotation)
205212
{

0 commit comments

Comments
 (0)