307 lines
15 KiB
C#
307 lines
15 KiB
C#
using adas_core.Domain.Models.MongoModels;
|
|
using MongoDB.Bson;
|
|
using MongoDB.Bson.IO;
|
|
using MongoDB.Bson.Serialization;
|
|
using MongoDB.Bson.Serialization.Serializers;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace adas_core.Domain.Models.BsonConverters;
|
|
|
|
/// <summary>
|
|
/// A BSON converter responsible for serializing and deserializing
|
|
/// <see cref="Dictionary{TKey, TValue}"/> instances with string keys and object values,
|
|
/// extending the base serializer infrastructure to support BSON format conversion.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This type specializes the generic base serializer for the specific dictionary shape
|
|
/// <c>Dictionary<string, object></c>, providing a focused implementation for
|
|
/// BSON-based persistence and data interchange scenarios.
|
|
/// </remarks>
|
|
public class DictionaryBsonConverter : SerializerBase<Dictionary<string, object>>
|
|
{
|
|
/// <summary>
|
|
/// Deserializes a BSON document into a dictionary where each top-level field name is mapped to its corresponding value.
|
|
/// Recognized keys (<c>STEPS</c>, <c>BEACONS</c>, <c>CAMERAS</c>, <c>RELAY</c>) are deserialized into their strongly-typed collections or objects, while unknown keys are deserialized based on the current BSON type (numeric values, strings, booleans, arrays of mixed primitive types, or generic BSON documents).
|
|
/// If the current BSON value is not a document, an empty dictionary is returned.
|
|
/// </summary>
|
|
/// <param name="context">The BSON deserialization context providing the reader used to traverse the BSON payload.</param>
|
|
/// <param name="args">The BSON deserialization arguments carrying additional configuration for the deserialization process.</param>
|
|
/// <returns>A dictionary containing the deserialized key/value pairs extracted from the BSON document.</returns>
|
|
public override Dictionary<string, object> Deserialize(BsonDeserializationContext context,
|
|
BsonDeserializationArgs args)
|
|
{
|
|
var reader = context.Reader;
|
|
var stepsDictionary = new Dictionary<string, object>();
|
|
|
|
if (reader.CurrentBsonType == BsonType.Document)
|
|
{
|
|
reader.ReadStartDocument();
|
|
|
|
while (reader.ReadBsonType() != BsonType.EndOfDocument)
|
|
{
|
|
var key = reader.ReadName();
|
|
|
|
switch (key.ToUpper())
|
|
{
|
|
case "STEPS":
|
|
var stepList = BsonSerializer.Deserialize<List<Step>>(reader);
|
|
stepsDictionary[key] = stepList;
|
|
break;
|
|
case "BEACONS":
|
|
var beaconList = BsonSerializer.Deserialize<List<LightBeacon>>(reader);
|
|
stepsDictionary[key] = beaconList;
|
|
break;
|
|
case "CAMERAS":
|
|
var cameraList = BsonSerializer.Deserialize<List<Camera>>(reader);
|
|
stepsDictionary[key] = cameraList;
|
|
break;
|
|
case "RELAY":
|
|
var relayList = BsonSerializer.Deserialize<Relay>(reader);
|
|
stepsDictionary[key] = relayList;
|
|
break;
|
|
|
|
default:
|
|
switch (reader.CurrentBsonType)
|
|
{
|
|
case BsonType.Int32:
|
|
case BsonType.Int64:
|
|
stepsDictionary[key] = BsonSerializer.Deserialize<long>(reader);
|
|
break;
|
|
case BsonType.Double:
|
|
stepsDictionary[key] = BsonSerializer.Deserialize<double>(reader);
|
|
break;
|
|
case BsonType.String:
|
|
stepsDictionary[key] = BsonSerializer.Deserialize<string>(reader);
|
|
break;
|
|
case BsonType.Boolean:
|
|
stepsDictionary[key] = BsonSerializer.Deserialize<bool>(reader);
|
|
break;
|
|
case BsonType.Array:
|
|
reader.ReadStartArray();
|
|
var arrayItems = new List<object>();
|
|
while (reader.ReadBsonType() != BsonType.EndOfDocument)
|
|
switch (reader.CurrentBsonType)
|
|
{
|
|
case BsonType.String:
|
|
arrayItems.Add(BsonSerializer.Deserialize<string>(reader));
|
|
break;
|
|
case BsonType.Int32:
|
|
arrayItems.Add(BsonSerializer.Deserialize<int>(reader));
|
|
break;
|
|
case BsonType.Int64:
|
|
arrayItems.Add(BsonSerializer.Deserialize<long>(reader));
|
|
break;
|
|
case BsonType.Boolean:
|
|
arrayItems.Add(BsonSerializer.Deserialize<bool>(reader));
|
|
break;
|
|
case BsonType.Double:
|
|
arrayItems.Add(BsonSerializer.Deserialize<double>(reader));
|
|
break;
|
|
case BsonType.ObjectId:
|
|
arrayItems.Add(BsonSerializer.Deserialize<ObjectId>(reader));
|
|
break;
|
|
case BsonType.DateTime:
|
|
arrayItems.Add(BsonSerializer.Deserialize<DateTime>(reader));
|
|
break;
|
|
|
|
default:
|
|
reader.SkipValue();
|
|
break;
|
|
}
|
|
|
|
reader.ReadEndArray();
|
|
stepsDictionary[key] = arrayItems;
|
|
break;
|
|
default:
|
|
var bsonDoc = BsonSerializer.Deserialize<BsonDocument>(reader);
|
|
stepsDictionary[key] = bsonDoc.ToDictionary();
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
reader.ReadEndDocument();
|
|
}
|
|
|
|
return stepsDictionary;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes a dictionary of string keys and object values into a BSON document. When a value's
|
|
/// type cannot be handled by the underlying key/value serializer, the corresponding key is written
|
|
/// with a null value as a fallback to preserve all dictionary entries.
|
|
/// </summary>
|
|
/// <param name="context">The BSON serialization context that provides the writer used to emit the document.</param>
|
|
/// <param name="args">The BSON serialization arguments associated with the current operation.</param>
|
|
/// <param name="value">The dictionary whose entries are written as fields of the BSON document.</param>
|
|
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args,
|
|
Dictionary<string, object> value)
|
|
{
|
|
var writer = context.Writer;
|
|
writer.WriteStartDocument();
|
|
|
|
foreach (var item in value)
|
|
{
|
|
if (TrySerializeKeyValuePair(item, writer)) continue;
|
|
|
|
// Si llegamos a este punto, encontramos un tipo no soportado.
|
|
writer.WriteName(item.Key);
|
|
writer.WriteNull(); // Escribe un valor null si el tipo no es soportado.
|
|
}
|
|
|
|
|
|
writer.WriteEndDocument();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to serialize a key-value pair to BSON format using the provided writer. Handles specific well-known keys (<c>steps</c>, <c>beacons</c>, <c>cameras</c>, <c>relay</c>, and <c>uiFontProperties</c>) with their strongly-typed models, and falls back to serializing nested dictionaries, JArrays, or primitive BSON values for any other key. Returns <c>false</c> when the value cannot be converted to a BSON value or when no matching case applies.
|
|
/// </summary>
|
|
/// <param name="item">The key-value pair to serialize, where the key determines the serialization strategy and the value holds the data to write.</param>
|
|
/// <param name="writer">The BSON writer used to emit the serialized output for the key-value pair.</param>
|
|
/// <returns><c>true</c> if the key-value pair was successfully serialized; otherwise, <c>false</c>.</returns>
|
|
/// <exception cref="BsonSerializationException">Thrown when a BSON array contains an element whose BSON type is not supported.</exception>
|
|
/// <exception cref="NotSupportedException">Thrown when the default branch encounters a BSON value type that is not supported.</exception>
|
|
private static bool TrySerializeKeyValuePair(KeyValuePair<string, object> item, IBsonWriter writer)
|
|
{
|
|
writer.WriteName(item.Key);
|
|
|
|
switch (item.Key)
|
|
{
|
|
case "steps":
|
|
if (item.Value is JArray jSteps)
|
|
{
|
|
var steps = jSteps.ToObject<List<Step>>();
|
|
BsonSerializer.Serialize(writer, typeof(List<Step>), steps);
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
case "beacons":
|
|
if (item.Value is JArray jBeaconConfigs)
|
|
{
|
|
var beaconConfigs = jBeaconConfigs.ToObject<List<LightBeacon>>();
|
|
BsonSerializer.Serialize(writer, typeof(List<LightBeacon>), beaconConfigs);
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
case "cameras":
|
|
if (item.Value is JArray jCamerasConfigs)
|
|
{
|
|
var cameraConfigs = jCamerasConfigs.ToObject<List<Camera>>();
|
|
BsonSerializer.Serialize(writer, typeof(List<Camera>), cameraConfigs);
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
case "relay":
|
|
if (item.Value is JObject jRelayConfigs)
|
|
{
|
|
var relayConfigs = jRelayConfigs.ToObject<Relay>();
|
|
BsonSerializer.Serialize(writer, relayConfigs);
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
case "uiFontProperties":
|
|
{
|
|
if (item.Value is JObject jUiFontData)
|
|
{
|
|
var uiFontData = jUiFontData.ToObject<UiFontData>();
|
|
BsonSerializer.Serialize(writer, uiFontData);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (item.Value is Dictionary<string, object> nestedDictionary)
|
|
{
|
|
var bsonDoc = new BsonDocument(nestedDictionary);
|
|
BsonSerializer.Serialize(writer, bsonDoc);
|
|
return true;
|
|
}
|
|
|
|
if (item.Value is JArray jArray)
|
|
{
|
|
var listObjects = jArray.ToObject<List<object>>();
|
|
BsonSerializer.Serialize(writer, listObjects);
|
|
return true;
|
|
}
|
|
|
|
// Para otros tipos primitivos, utilizamos BsonValue.Create
|
|
try
|
|
{
|
|
var bsonValue = BsonValue.Create(item.Value);
|
|
switch (bsonValue.BsonType)
|
|
{
|
|
case BsonType.String:
|
|
writer.WriteString(bsonValue.AsString);
|
|
break;
|
|
|
|
case BsonType.Int32:
|
|
writer.WriteInt32(bsonValue.AsInt32);
|
|
break;
|
|
|
|
case BsonType.Int64:
|
|
writer.WriteInt64(bsonValue.AsInt64);
|
|
break;
|
|
|
|
case BsonType.Double:
|
|
writer.WriteDouble(bsonValue.AsDouble);
|
|
break;
|
|
|
|
case BsonType.Boolean:
|
|
writer.WriteBoolean(bsonValue.AsBoolean);
|
|
break;
|
|
|
|
case BsonType.Array:
|
|
writer.WriteStartArray();
|
|
foreach (var arrayValue in bsonValue.AsBsonArray)
|
|
switch (arrayValue.BsonType)
|
|
{
|
|
case BsonType.String:
|
|
writer.WriteString(arrayValue.AsString);
|
|
break;
|
|
|
|
case BsonType.Int32:
|
|
writer.WriteInt32(arrayValue.AsInt32);
|
|
break;
|
|
|
|
case BsonType.Int64:
|
|
writer.WriteInt64(arrayValue.AsInt64);
|
|
break;
|
|
|
|
case BsonType.Double:
|
|
writer.WriteDouble(arrayValue.AsDouble);
|
|
break;
|
|
|
|
case BsonType.Boolean:
|
|
writer.WriteBoolean(arrayValue.AsBoolean);
|
|
break;
|
|
|
|
default:
|
|
throw new BsonSerializationException(
|
|
$"No se puede serializar el tipo Bson en el array: {arrayValue.BsonType}");
|
|
}
|
|
|
|
writer.WriteEndArray();
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException($"BsonType {bsonValue.BsonType} no es compatible.");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} |