using System.Collections.Immutable;
namespace Dpz.Core.Service.OpenIddictStores;
internal static class OpenIddictBsonPropertySerializer
{
private static readonly JsonSerializerOptions SerializerOptions = new(
JsonSerializerDefaults.Web
);
public static ImmutableDictionary<string, JsonElement> ToProperties(BsonDocument? document)
{
if (document is null || document.ElementCount == 0)
{
return ImmutableDictionary<string, JsonElement>.Empty;
}
var builder = ImmutableDictionary.CreateBuilder<string, JsonElement>(
StringComparer.Ordinal,
JsonElementValueComparer.Instance
);
foreach (var element in document.Elements)
{
builder[element.Name] = ToJsonElement(element.Value);
}
return builder.ToImmutable();
}
public static BsonDocument? ToDocument(ImmutableDictionary<string, JsonElement> properties)
{
if (properties.Count == 0)
{
return null;
}
var document = new BsonDocument();
foreach (var (key, value) in properties)
{
document[key] = ToBsonValue(value);
}
return document;
}
private static JsonElement ToJsonElement(BsonValue value)
{
using var document = JsonDocument.Parse(
JsonSerializer.Serialize(ToClrValue(value), SerializerOptions)
);
return document.RootElement.Clone();
}
private static object? ToClrValue(BsonValue value)
{
if (value.IsBsonNull || value.IsBsonUndefined)
{
return null;
}
if (value.IsBsonDocument)
{
return value.AsBsonDocument.Elements.ToDictionary(
element => element.Name,
element => ToClrValue(element.Value),
StringComparer.Ordinal
);
}
if (value.IsBsonArray)
{
return value.AsBsonArray.Select(ToClrValue).ToArray();
}
if (value.IsBoolean)
{
return value.AsBoolean;
}
if (value.IsString)
{
return value.AsString;
}
if (value.IsInt32)
{
return value.AsInt32;
}
if (value.IsInt64)
{
return value.AsInt64;
}
if (value.IsDouble)
{
return value.AsDouble;
}
if (value.IsDecimal128)
{
return Decimal128.ToDecimal(value.AsDecimal128);
}
if (value.IsValidDateTime)
{
return value.ToUniversalTime();
}
if (value.IsObjectId)
{
return value.AsObjectId.ToString();
}
return value.ToString();
}
private static BsonValue ToBsonValue(JsonElement element)
{
return element.ValueKind switch
{
JsonValueKind.Object => ToBsonDocument(element),
JsonValueKind.Array => ToBsonArray(element),
JsonValueKind.String => new BsonString(element.GetString()),
JsonValueKind.Number => ToBsonNumber(element),
JsonValueKind.True => BsonBoolean.True,
JsonValueKind.False => BsonBoolean.False,
_ => BsonNull.Value,
};
}
private static BsonDocument ToBsonDocument(JsonElement element)
{
var document = new BsonDocument();
foreach (var property in element.EnumerateObject())
{
document[property.Name] = ToBsonValue(property.Value);
}
return document;
}
private static BsonArray ToBsonArray(JsonElement element)
{
var array = new BsonArray();
foreach (var item in element.EnumerateArray())
{
array.Add(ToBsonValue(item));
}
return array;
}
private static BsonValue ToBsonNumber(JsonElement element)
{
if (element.TryGetInt32(out var int32))
{
return new BsonInt32(int32);
}
if (element.TryGetInt64(out var int64))
{
return new BsonInt64(int64);
}
if (element.TryGetDecimal(out var decimalValue))
{
return new BsonDecimal128(decimalValue);
}
return new BsonDouble(element.GetDouble());
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个用于在 BSON 格式(MongoDB 使用的二进制 JSON 格式)和 System.Text.Json 的 JsonElement 之间进行双向转换的静态工具类,主要用于 OpenIddict 存储层的数据序列化。
核心功能
1. ToProperties 方法
public static ImmutableDictionary<string, JsonElement> ToProperties(BsonDocument? document)
- 功能:将 BSON 文档转换为不可变字典(键为字符串,值为 JsonElement)
- 处理逻辑:
- 如果文档为空或没有元素,返回空字典
- 遍历 BSON 文档的每个元素,将其转换为 JsonElement
- 使用顺序字符串比较器和自定义的 JsonElement 比较器
2. ToDocument 方法
public static BsonDocument? ToDocument(ImmutableDictionary<string, JsonElement> properties)
- 功能:将不可变字典转换回 BSON 文档
- 处理逻辑:
- 如果字典为空,返回 null
- 遍历字典的每个键值对,将 JsonElement 转换为 BsonValue
3. ToJsonElement 方法(私有)
private static JsonElement ToJsonElement(BsonValue value)
- 功能:将单个 BSON 值转换为 JsonElement
- 转换策略:
- 先将 BsonValue 转换为 CLR 对象(通过
ToClrValue) - 使用 JsonSerializer 序列化为 JSON 字符串
- 解析为 JsonDocument 并克隆根元素
- 先将 BsonValue 转换为 CLR 对象(通过
4. ToClrValue 方法(私有)
private static object? ToClrValue(BsonValue value)
- 功能:将 BSON 值转换为对应的 CLR(公共语言运行时)类型
- 支持的类型映射:
BsonNull/BsonUndefined→nullBsonDocument→Dictionary<string, object?>(递归处理)BsonArray→object[](递归处理)Boolean→boolString→stringInt32→intInt64→longDouble→doubleDecimal128→decimalDateTime→ UTC 时间ObjectId→string(转为字符串表示)- 其他 → 调用
ToString()
5. ToBsonValue 方法(私有)
private static BsonValue ToBsonValue(JsonElement element)
- 功能:将 JsonElement 转换为 BSON 值
- 使用模式匹配处理不同类型:
Object→ BsonDocumentArray→ BsonArrayString→ BsonStringNumber→ 根据精度转换(Int32/Int64/Decimal128/Double)True/False→ BsonBoolean- 其他 → BsonNull
6. 辅助转换方法
- ToBsonDocument:递归转换 JSON 对象
- ToBsonArray:递归转换 JSON 数组
- ToBsonNumber:智能识别数字类型(优先使用较小类型)
设计亮点
- 不可变性:使用
ImmutableDictionary保证线程安全 - 类型安全:完整的类型映射,避免数据丢失
- 递归处理:支持嵌套的复杂数据结构
- 智能类型推断:数字类型按精度自动选择最合适的存储格式
- JSON 标准兼容:使用 Web 默认序列化选项
使用场景
该类主要用于 OpenIddict(OAuth 2.0/OpenID Connect 框架)的 MongoDB 存储实现中,用于存储和检索授权、令牌等配置属性。
AI 正在分析代码…
评论加载中...