Skip to content
Merged
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
98 changes: 98 additions & 0 deletions src/Kuddle.Net.Tests/Serialization/ObjectMapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,104 @@ public async Task Deserialize_FlattenedCollection_CollectsAllMatchingNodes()
await Assert.That(result.FlattenedServers[1].Host).IsEqualTo("remote");
}

[Test]
public async Task Serialize_WithSimpleCollectionNodeNames_True_UseDashNodeNames()
{
// Arrange
var model = new CollectionModel
{
WrappedPlugins = [new() { Name = "Auth" }],
FlattenedServers = [new() { Host = "localhost" }],
};

var options = new KdlSerializerOptions { SimpleCollectionNodeNames = true };

// Act
var kdl = KdlSerializer.Serialize(model, options);

// Assert - should use dash (-) for collection items
await Assert.That(kdl).Contains("plugins {");
await Assert.That(kdl).Contains("- Auth");
}

[Test]
public async Task Serialize_WithSimpleCollectionNodeNames_False_UseTypedNodeNames()
{
// Arrange
var model = new CollectionModel
{
WrappedPlugins = [new() { Name = "Auth" }],
FlattenedServers = [new() { Host = "localhost" }],
};

var options = new KdlSerializerOptions { SimpleCollectionNodeNames = false };

// Act
var kdl = KdlSerializer.Serialize(model, options);

// Assert - should use typed node names
await Assert.That(kdl).Contains("plugins {");
await Assert.That(kdl).Contains("plugin-info Auth");
}

[Test]
public async Task RoundTrip_SerializeAndDeserialize_WithSimpleCollectionNodeNames_True()
{
// Arrange
var originalModel = new CollectionModel
{
WrappedPlugins = [new() { Name = "Auth" }, new() { Name = "Logging" }],
FlattenedServers = [new() { Host = "localhost" }, new() { Host = "remote" }],
};

var options = new KdlSerializerOptions
{
SimpleCollectionNodeNames = true,
UnwrapRoot = true,
};

// Act
var kdl = KdlSerializer.Serialize(originalModel, options);
var deserializedModel = KdlSerializer.Deserialize<CollectionModel>(kdl, options);

// Assert
await Assert.That(deserializedModel.WrappedPlugins).Count().IsEqualTo(2);
await Assert.That(deserializedModel.WrappedPlugins[0].Name).IsEqualTo("Auth");
await Assert.That(deserializedModel.WrappedPlugins[1].Name).IsEqualTo("Logging");
await Assert.That(deserializedModel.FlattenedServers).Count().IsEqualTo(2);
await Assert.That(deserializedModel.FlattenedServers[0].Host).IsEqualTo("localhost");
await Assert.That(deserializedModel.FlattenedServers[1].Host).IsEqualTo("remote");
}

[Test]
public async Task RoundTrip_SerializeAndDeserialize_WithSimpleCollectionNodeNames_False()
{
// Arrange
var originalModel = new CollectionModel
{
WrappedPlugins = [new() { Name = "Auth" }, new() { Name = "Logging" }],
FlattenedServers = [new() { Host = "localhost" }, new() { Host = "remote" }],
};

var options = new KdlSerializerOptions
{
SimpleCollectionNodeNames = false,
UnwrapRoot = false,
};

// Act
var kdl = KdlSerializer.Serialize(originalModel, options);
var deserializedModel = KdlSerializer.Deserialize<CollectionModel>(kdl, options);

// Assert
await Assert.That(deserializedModel.WrappedPlugins).Count().IsEqualTo(2);
await Assert.That(deserializedModel.WrappedPlugins[0].Name).IsEqualTo("Auth");
await Assert.That(deserializedModel.WrappedPlugins[1].Name).IsEqualTo("Logging");
await Assert.That(deserializedModel.FlattenedServers).Count().IsEqualTo(2);
await Assert.That(deserializedModel.FlattenedServers[0].Host).IsEqualTo("localhost");
await Assert.That(deserializedModel.FlattenedServers[1].Host).IsEqualTo("remote");
}

#endregion

#region Test Models
Expand Down
9 changes: 7 additions & 2 deletions src/Kuddle.Net/Serialization/KdlSerializerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ namespace Kuddle.Serialization;
/// </summary>
public record KdlSerializerOptions
{
/// <summary>
/// Default options instance.
/// </summary>
public static KdlSerializerOptions Default { get; } = new();

/// <summary>
/// Whether to ignore null values when serializing. Default is true.
/// </summary>
Expand All @@ -21,8 +26,8 @@ public record KdlSerializerOptions
public bool WriteTypeAnnotations { get; init; } = true;

/// <summary>
/// Default options instance.
/// Whether to use simple dash (-) for collection item node names instead of the mapped type name. Default is true.
/// </summary>
public static KdlSerializerOptions Default { get; } = new();
public bool SimpleCollectionNodeNames { get; init; } = true;
public bool UnwrapRoot { get; init; } = false;
}
11 changes: 11 additions & 0 deletions src/Kuddle.Net/Serialization/ObjectDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ internal static T DeserializeDocument<T>(KdlDocument doc, KdlSerializerOptions?

var mapping = KdlTypeMapping.For<T>();
var instance = new T();
if (options?.UnwrapRoot == false)
{
if (doc.Nodes.Count != 1)
{
throw new KuddleSerializationException(
$"Expected exactly 1 root node, but found {doc.Nodes.Count}."
);
}
worker.MapNodeToObject(doc.Nodes.First(), instance, mapping);
return instance;
}

if (mapping.Arguments.Count > 0 || mapping.Properties.Count > 0)
{
Expand Down
19 changes: 13 additions & 6 deletions src/Kuddle.Net/Serialization/ObjectSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ internal static KdlDocument SerializeDocument<T>(T? instance, KdlSerializerOptio
// Map positional arguments to anonymous nodes
doc.Nodes.Add(new KdlNode(KdlValue.From("-")) { Entries = [arg] });
}
if (rootNode.Children != null)
{
doc.Nodes.AddRange(rootNode.Children.Nodes);
}
}
if (rootNode.Children != null)
{
doc.Nodes.AddRange(rootNode.Children.Nodes);
}
}
else
Expand Down Expand Up @@ -198,8 +198,15 @@ private IEnumerable<KdlNode> SerializeCollection(IEnumerable enumerable, KdlMemb
}
}

private static string GetDefaultNodeName(object item) =>
item.GetType().IsKdlScalar ? "-" : KdlTypeMapping.For(item.GetType()).NodeName;
private string GetDefaultNodeName(object item)
{
if (item.GetType().IsKdlScalar)
return "-";

return _options.SimpleCollectionNodeNames
? "-"
: KdlTypeMapping.For(item.GetType()).NodeName;
}

private KdlNode MapToNode(object item, string kdlName, string? typeAnnotation)
{
Expand Down