programing

Bson 직렬화사전을 BSON에 직렬화할 때 예외 발생

abcjava 2023. 6. 25. 18:12
반응형

Bson 직렬화사전을 BSON에 직렬화할 때 예외 발생

저는 최근에 더 이상 사용되지 않는 v1.9에서 새로운 MongoDBC# 드라이버 v2.0으로 이동했습니다.

자, 제가 사전이 있는 수업을 연재할 때, 저는 가끔 다음과 마주칩니다.BsonSerializationException:

몽고DB.Bson.Bson 직렬화예외:사전 표현을 사용하는 경우.문서 키 값은 문자열로 직렬화해야 합니다.

다음은 최소한의 복제품입니다.

class Hamster
{
    public ObjectId Id { get; private set; }
    public Dictionary<DateTime,int> Dictionary { get; private set; }
    public Hamster()
    {
        Id = ObjectId.GenerateNewId();
        Dictionary = new Dictionary<DateTime, int>();
        Dictionary[DateTime.UtcNow] = 0;
    }
}

static void Main()
{
    Console.WriteLine(new Hamster().ToJson());
}

문제는 새 드라이버가 사전을 기본적으로 문서로 직렬화한다는 것입니다.

MongoDBC# 드라이버는 사전을 직렬화하는 세 가지 방법이 있습니다.Document,ArrayOfArrays&ArrayOfDocuments(자세한 내용은 문서 참조).문서로 직렬화될 때 사전 키는 몇 가지 제한 사항이 있는 BSON 요소의 이름입니다(예: 오류에서 알 수 있듯이 문자열로 직렬화되어야 함).

이 경우 사전의 키는DateTimes는 문자열로 직렬화되지 않지만,Date그래서 우리는 다른 것을 선택해야 합니다.DictionaryRepresentation.

이 특정 속성의 직렬화를 변경하려면 다음을 사용할 수 있습니다.BsonDictionaryOptions다른 속성DictionaryRepresentation:

[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
public Dictionary<DateTime, int> Dictionary { get; private set; }

하지만 우리는 문제가 있는 모든 멤버들에게 개별적으로 그렇게 해야 합니다.적용 방법DictionaryRepresentation모든 관련 회원들에게 우리는 새로운 협약을 시행할 수 있습니다.

class DictionaryRepresentationConvention : ConventionBase, IMemberMapConvention
{
    private readonly DictionaryRepresentation _dictionaryRepresentation;
    public DictionaryRepresentationConvention(DictionaryRepresentation dictionaryRepresentation)
    {
        _dictionaryRepresentation = dictionaryRepresentation;
    }
    public void Apply(BsonMemberMap memberMap)
    {
        memberMap.SetSerializer(ConfigureSerializer(memberMap.GetSerializer()));
    }
    private IBsonSerializer ConfigureSerializer(IBsonSerializer serializer)
    {
        var dictionaryRepresentationConfigurable = serializer as IDictionaryRepresentationConfigurable;
        if (dictionaryRepresentationConfigurable != null)
        {
            serializer = dictionaryRepresentationConfigurable.WithDictionaryRepresentation(_dictionaryRepresentation);
        }

        var childSerializerConfigurable = serializer as IChildSerializerConfigurable;
        return childSerializerConfigurable == null
            ? serializer
            : childSerializerConfigurable.WithChildSerializer(ConfigureSerializer(childSerializerConfigurable.ChildSerializer));
    }
} 

다음과 같이 등록합니다.

ConventionRegistry.Register(
    "DictionaryRepresentationConvention",
    new ConventionPack {new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays)},
    _ => true);

위의 답변은 훌륭하지만 안타깝게도 StackOverflow를 트리거합니다.특정 재귀 개체 계층에 대한 예외 - 여기 약간 개선된 최신 버전이 있습니다.

public class DictionaryRepresentationConvention : ConventionBase, IMemberMapConvention
{
    private readonly DictionaryRepresentation _dictionaryRepresentation;

    public DictionaryRepresentationConvention(DictionaryRepresentation dictionaryRepresentation = DictionaryRepresentation.ArrayOfDocuments)
    {
        // see http://mongodb.github.io/mongo-csharp-driver/2.2/reference/bson/mapping/#dictionary-serialization-options

        _dictionaryRepresentation = dictionaryRepresentation;
    }

    public void Apply(BsonMemberMap memberMap)
    {
        memberMap.SetSerializer(ConfigureSerializer(memberMap.GetSerializer(),Array.Empty<IBsonSerializer>()));
    }

    private IBsonSerializer ConfigureSerializer(IBsonSerializer serializer, IBsonSerializer[] stack)
    {
        if (serializer is IDictionaryRepresentationConfigurable dictionaryRepresentationConfigurable)
        {
            serializer = dictionaryRepresentationConfigurable.WithDictionaryRepresentation(_dictionaryRepresentation);
        }

        if (serializer is IChildSerializerConfigurable childSerializerConfigurable)
        {
            if (!stack.Contains(childSerializerConfigurable.ChildSerializer))
            {
                var newStack = stack.Union(new[] { serializer }).ToArray();
                var childConfigured = ConfigureSerializer(childSerializerConfigurable.ChildSerializer, newStack);
                return childSerializerConfigurable.WithChildSerializer(childConfigured);
            }
        }

        return serializer;
    }

저처럼 클래스의 단일 필드에 이 기능을 적용하기를 원하신다면, 저는 이렇게 할 수 있었습니다(다른 답변 덕분에).

BsonClassMap.RegisterClassMap<TestClass>(cm =>
{
    cm.AutoMap();
    var memberMap = cm.GetMemberMap(x => x.DictionaryField);
    var serializer = memberMap.GetSerializer();
    if (serializer is IDictionaryRepresentationConfigurable dictionaryRepresentationSerializer)
        serializer = dictionaryRepresentationSerializer.WithDictionaryRepresentation(DictionaryRepresentation.ArrayOfDocuments);
    memberMap.SetSerializer(serializer);
});

또는 확장 방법으로:

BsonClassMap.RegisterClassMap<TestClass>(cm =>
{
    cm.AutoMap();
    cm.SetDictionaryRepresentation(x => x.DictionaryField, DictionaryRepresentation.ArrayOfDocuments);
});

public static class MapHelpers
{
    public static BsonClassMap<T> SetDictionaryRepresentation<T, TMember>(this BsonClassMap<T> classMap, Expression<Func<T,TMember>> memberLambda, DictionaryRepresentation representation)
    {
        var memberMap = classMap.GetMemberMap(memberLambda);
        var serializer = memberMap.GetSerializer();
        if (serializer is IDictionaryRepresentationConfigurable dictionaryRepresentationSerializer)
            serializer = dictionaryRepresentationSerializer.WithDictionaryRepresentation(representation);
        memberMap.SetSerializer(serializer);
        return classMap;
    }
}

위의 코드로 시스템을 얻습니다.잘못된 작업예외: 'ValueFactory가 이 인스턴스의 Value 속성에 액세스하려고 시도했습니다.'

List<> 속성과 관련이 있습니다.

언급URL : https://stackoverflow.com/questions/28111846/bsonserializationexception-when-serializing-a-dictionarydatetime-t-to-bson

반응형