dbMango/Rms.Risk.Mango.Pivot.Core/ArrayBasedPivotDataSerializer.cs
Alexander Shabarshov 2a7a24c9e7 Initial contribution
2025-11-03 14:43:26 +00:00

157 lines
5.5 KiB
C#

/*
* dbMango
*
* Copyright 2025 Deutsche Bank AG
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Runtime.Serialization;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace Rms.Risk.Mango.Pivot.Core;
internal class ArrayBasedPivotDataSerializer : SerializerBase<ArrayBasedPivotData>
{
public static readonly DateTime UnixEpoch = new(
1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static ulong ToMillisecondsSinceUnixEpoch(DateTime dateTimeUtc) => (ulong)(dateTimeUtc - UnixEpoch).TotalMilliseconds;
public override void Serialize( BsonSerializationContext context, BsonSerializationArgs args, ArrayBasedPivotData value )
{
if ( value == null )
throw new ArgumentNullException( nameof(value), "ArrayBasedPivotData instance is null" );
if ( string.IsNullOrWhiteSpace( value.Id ) )
throw new SerializationException( "Id is not set for ArrayBasedPivotData instance" );
var headers = value.Headers;
if ( headers.Count == 0 )
throw new SerializationException("Headers count is zero for ArrayBasedPivotData instance");
context.Writer.WriteStartDocument();
context.Writer.WriteName("_id");
context.Writer.WriteString(value.Id);
context.Writer.WriteName("ExpireAt");
context.Writer.WriteDateTime((long)ToMillisecondsSinceUnixEpoch(value.ExpireAt));
context.Writer.WriteName("Headers");
context.Writer.WriteStartArray();
foreach ( var header in headers )
context.Writer.WriteString( header );
context.Writer.WriteEndArray();
context.Writer.WriteName( "data" );
context.Writer.WriteStartArray();
for ( var row = 0; row < value.Count; row ++ )
{
context.Writer.WriteStartArray();
for ( var col = 0; col < headers.Count; col++ )
{
var data = value.Get( col, row );
if ( data == null )
{
context.Writer.WriteNull();
continue;
}
var t = data.GetType();
if (t == typeof(double))
context.Writer.WriteDouble((double)data);
else if (t == typeof(string))
context.Writer.WriteString((string)data);
else if (t == typeof(int))
context.Writer.WriteInt32((int)data);
else if (t == typeof(long))
context.Writer.WriteInt64((long)data);
else if (t == typeof(DateTime))
context.Writer.WriteDateTime((long)ToMillisecondsSinceUnixEpoch((DateTime)data));
else if (t == typeof(bool))
context.Writer.WriteBoolean((bool)data);
else
context.Writer.WriteNull();
}
context.Writer.WriteEndArray();
}
context.Writer.WriteEndArray();
context.Writer.WriteEndDocument();
}
public override ArrayBasedPivotData Deserialize( BsonDeserializationContext context, BsonDeserializationArgs args )
{
context.Reader.ReadStartDocument();
ExpectName(context, "_id");
var id = context.Reader.ReadString();
ExpectName(context, "ExpireAt");
context.Reader.ReadDateTime();
ExpectName(context, "Headers");
context.Reader.ReadStartArray();
var headers = new List<string>();
while (context.Reader.ReadBsonType() != BsonType.EndOfDocument)
headers.Add( context.Reader.ReadString() );
context.Reader.ReadEndArray();
var data = new ArrayBasedPivotData( headers ) { Id = id };
ExpectName(context, "data");
context.Reader.ReadStartArray();
while (context.Reader.ReadBsonType() != BsonType.EndOfDocument) // rows
{
context.Reader.ReadStartArray();
var val = new object[headers.Count];
var col = 0;
while (context.Reader.ReadBsonType() != BsonType.EndOfDocument) // columns
{
var v = BsonValueSerializer.Instance.Deserialize(context);
try
{
val[col] = BsonTypeMapper.MapToDotNetValue(v);
}
catch ( Exception )
{
v[col] = null;
}
col += 1;
}
context.Reader.ReadEndArray();
data.Add( val );
}
context.Reader.ReadEndArray();
context.Reader.ReadEndDocument();
return data;
}
private static void ExpectName( BsonDeserializationContext context, string elementExpected )
{
var name = context.Reader.ReadName();
if ( name != elementExpected )
throw new SerializationException( $"{elementExpected} expected, but got {name}" );
}
}