Skip to content

Commit

Permalink
Fix nesting for objects
Browse files Browse the repository at this point in the history
  • Loading branch information
tspence committed Aug 5, 2024
1 parent 21def31 commit 1c2e55f
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 8 deletions.
17 changes: 15 additions & 2 deletions src/CSV.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ internal static string ItemsToCsv(IEnumerable items, CSVSettings settings, char[
if (item is string)
{
s = item as string;
}
if (item is DateTime)
}
else if (item is DateTime)
{
s = ((DateTime)item).ToString(settings.DateTimeFormat);
}
Expand Down Expand Up @@ -429,6 +429,19 @@ internal static string ItemsToCsv(IEnumerable items, CSVSettings settings, char[
break;
}
}
else if (itemType.IsClass && settings.NestedObjectBehavior == ObjectOptions.RecursiveSerialization)
{
var nestedItems = new List<object>();
foreach (var field in itemType.GetFields())
{
nestedItems.Add(field.GetValue(item));
}
foreach (var prop in itemType.GetProperties())
{
nestedItems.Add(prop.GetValue(item, null));
}
s = ItemsToCsv(nestedItems, settings, riskyChars, forceQualifierTypes);
}
else
{
s = item.ToString();
Expand Down
25 changes: 23 additions & 2 deletions src/CSVSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace CSVFile
public enum ArrayOptions
{
/// <summary>
/// Use built-in string conversion, which renders arrays as `MyObject[]`
/// Use built-in string conversion, which renders arrays as `MyNamespace.MyObject[]`
/// </summary>
ToString,

Expand All @@ -29,6 +29,22 @@ public enum ArrayOptions
/// </summary>
CountItems,

/// <summary>
/// Serialize child arrays recursively using the same settings
/// </summary>
RecursiveSerialization,
}

/// <summary>
/// Defines the behavior of CSV Serialization when a nested object (class) is encountered
/// </summary>
public enum ObjectOptions
{
/// <summary>
/// Use built-in string conversion, which renders as `MyNamespace.MyObject`
/// </summary>
ToString,

/// <summary>
/// Serialize child objects recursively using the same settings
/// </summary>
Expand Down Expand Up @@ -160,10 +176,15 @@ public class CSVSettings
public string DateTimeFormat { get; set; } = "o";

/// <summary>
/// The behavior to use when serializing a column of an array type
/// The behavior to use when serializing a column that is an array or enumerable type
/// </summary>
public ArrayOptions NestedArrayBehavior { get; set; } = ArrayOptions.ToString;

/// <summary>
/// The behavior to use when serializing a column that is a class
/// </summary>
public ObjectOptions NestedObjectBehavior { get; set; } = ObjectOptions.ToString;

/// <summary>
/// Standard comma-separated value (CSV) file settings
/// </summary>
Expand Down
56 changes: 52 additions & 4 deletions tests/SerializationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ public class TestClassThree
public List<Guid> NullableList { get; set; }
}

public class TestClassFour
{
public string Name { get; set; }
public TestClassTwo Details { get; set; }
}

[Test]
public void TestObjectSerialization()
{
Expand Down Expand Up @@ -136,10 +142,7 @@ public void TestNullSerialization()
}

/// <summary>
/// Arrays and child objects aren't well suited for complex serialization within a CSV file.
/// However, we have options:
/// * ToString just converts it to "MyClass[]"
/// * CountItems just produces the number of elements in the array
/// Tests that validate whether we can serialize arrays within arrays
/// </summary>
[Test]
public void TestArraySerialization()
Expand Down Expand Up @@ -186,6 +189,51 @@ public void TestArraySerialization()
+ $"Test,\"a,b,c\",\"1,2,3\",\"True,False,True,False\",,NULL{Environment.NewLine}", recursiveCsv);
}

/// <summary>
/// Tests that validate whether we can serialize objects within arrays
/// </summary>
[Test]
public void TestNestedObjectSerialization()
{
var list = new List<TestClassFour>();
list.Add(new TestClassFour()
{
Name = "Non-Null Test",
Details = new TestClassTwo()
{
FirstColumn = "Hello World!",
SecondColumn = 42,
ThirdColumn = EnumTestType.Third,
}
});
list.Add(new TestClassFour()
{
Name = "Null Test",
Details = null
});

// Serialize to a CSV string using ToString
// This was the default behavior in CSVFile 3.1.2 and earlier - it's pretty ugly!
var options = new CSVSettings()
{
HeaderRowIncluded = true,
NestedObjectBehavior = ObjectOptions.ToString,
NullToken = "NULL",
AllowNull = true,
};
var toStringCsv = CSV.Serialize(list, options);
Assert.AreEqual($"Name,Details{Environment.NewLine}"
+ $"Non-Null Test,CSVTestSuite.SerializationTest+TestClassTwo{Environment.NewLine}"
+ $"Null Test,NULL{Environment.NewLine}", toStringCsv);

// Serialize to a CSV string using counts
options.NestedObjectBehavior = ObjectOptions.RecursiveSerialization;
var recursiveCsv = CSV.Serialize(list, options);
Assert.AreEqual($"Name,Details{Environment.NewLine}"
+ $"Non-Null Test,\"Hello World!,42,Third\"{Environment.NewLine}"
+ $"Null Test,NULL{Environment.NewLine}", recursiveCsv);
}

[Test]
public void TestCaseInsensitiveDeserializer()
{
Expand Down

0 comments on commit 1c2e55f

Please sign in to comment.