From 7e29b1e1bd412177b83ed6b33f7a552481553b32 Mon Sep 17 00:00:00 2001 From: Stefan Eriksson Date: Fri, 26 Jan 2024 09:22:17 +0100 Subject: [PATCH] Serialize nullable types #465 --- .gitignore | 1 + .../Serialisation/ParquetSerializerTest.cs | 20 +++++++++++++++++-- src/Parquet/Serialization/TypeExtensions.cs | 10 +++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index eaa3d29f..8178dd2d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ BenchmarkDotNet.Artifacts/ Parquet.sln.DotSettings.user launchSettings.json .DS_Store +*.user diff --git a/src/Parquet.Test/Serialisation/ParquetSerializerTest.cs b/src/Parquet.Test/Serialisation/ParquetSerializerTest.cs index f0f20873..e03fa0fe 100644 --- a/src/Parquet.Test/Serialisation/ParquetSerializerTest.cs +++ b/src/Parquet.Test/Serialisation/ParquetSerializerTest.cs @@ -162,6 +162,13 @@ class NullableRecord : Record { public double? ParentMeterValue { get; set; } public decimal? ParentResolution { get; set; } + + public DateTime? ParentTime { get; set; } + + public Interval? ParentInterval { get; set; } + + public TimeSpan? ParentTimeSpan { get; set; } + } [Fact] @@ -172,8 +179,12 @@ public async Task Atomics_Nullable_Serde() { EventName = i % 2 == 0 ? "on" : "off", MeterValue = i, ParentId = (i % 4 == 0) ? null : i, + ExternalId = Guid.NewGuid(), ParentMeterValue = (i % 5 == 0) ? null : (double?)i, - ParentResolution = (i % 6 == 0) ? null : (decimal?)i + ParentResolution = (i % 6 == 0) ? null : (decimal?)i, + ParentTime = (i % 5 == 0) ? null : DateTime.UtcNow.AddSeconds(i), + ParentInterval = (i % 5 == 0) ? null : new Interval(i, i, i), + ParentTimeSpan = (i % 5 == 0) ? null : TimeSpan.FromSeconds(i) }).ToList(); await Compare(data); @@ -187,7 +198,12 @@ public async Task Atomics_Nullable_Serde_Dict() { ["EventName"] = i % 2 == 0 ? "on" : "off", ["MeterValue"] = (double)i, ["ParentId"] = (i % 4 == 0) ? null : i, - ["ExternalId"] = Guid.NewGuid() + ["ExternalId"] = Guid.NewGuid(), + ["ParentMeterValue"] = (i % 5 == 0) ? null : (double?)i, + ["ParentResolution"] = (i % 6 == 0) ? null : (decimal?)i, + ["ParentTime"] = (i % 5 == 0) ? null : DateTime.UtcNow.AddSeconds(i), + ["ParentInterval"] = (i % 5 == 0) ? null : new Interval(i, i, i), + ["ParentTimeSpan"] = (i % 5 == 0) ? null : TimeSpan.FromSeconds(i) }).ToList(); await DictCompare(data); diff --git a/src/Parquet/Serialization/TypeExtensions.cs b/src/Parquet/Serialization/TypeExtensions.cs index 12a38477..58a57a80 100644 --- a/src/Parquet/Serialization/TypeExtensions.cs +++ b/src/Parquet/Serialization/TypeExtensions.cs @@ -127,7 +127,7 @@ private static Field ConstructDataField(string name, string propertyName, Type t Field r; bool? isNullable = member == null ? null - : member.IsRequired ? false : null; + : member.IsRequired ? false : IsNullable(t); if(t == typeof(DateTime) || t == typeof(DateTime?)) { ParquetTimestampAttribute? tsa = member?.TimestampAttribute; @@ -165,6 +165,14 @@ private static Field ConstructDataField(string name, string propertyName, Type t return r; } + static bool IsNullable(Type type) { + if(!type.IsValueType) + return true; // ref-type + if(Nullable.GetUnderlyingType(type) != null) + return true; // Nullable + return false; // value-type + } + private static MapField ConstructMapField(string name, string propertyName, Type tKey, Type tValue, bool forWriting) {