Newtonsoft.Json-自引用循环,但DTO似乎没有一个

时间:2018-09-18 19:03:14

标签: c# asp.net json.net

我创建了一个包含另一个类的类。当在对象上调用JsonConvert.SerializeObject(translatedResponse)时,Newtonsoft告诉我有一个自引用循环。在尝试了各种方法之后,我更改了名称空间名称(即“ DocumentGenerationParticipant”,并为复数类的List创建了一个DocumentGenerationParticipantResult,而不是使用单数版本,该单数版本还是/ participant终结点的DTO。没看到为什么JSON序列化无法解决这个问题,并且我没有看到循环引用吗?我错过了什么吗?

使用以下DTO的Program.CS:

using ConsoleApp4.DocumentGeneration;
using ConsoleApp4.DocumentGeneration.DocumentGenerationParticipant;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace ConsoleApp4
{
    static class Program
    {
        static void Main(string[] args)
        {
            var list = new List<DocumentGenerationParticipantResponse>()
            {
                new DocumentGenerationParticipantResponse("testName","testEmail","testUri","testRole"),
                new DocumentGenerationParticipantResponse("testName","testEmail","testUri","testRole"),
                new DocumentGenerationParticipantResponse("testName","testEmail","testUri","testRole")
            };
            var response = new DocumentGenerationParticipantsResponse(list);
            var result = JsonConvert.SerializeObject(response);
            Console.WriteLine(result);
        }
    }
}

DocumentGenerationParticipantResponse:

using System.Collections.Generic;

namespace ConsoleApp4.DocumentGeneration
{
    public class DocumentGenerationParticipantResponse
    {
        public string Name { get; }

        public string Email { get; }

        public string AccessUri { get; }

        public string Role { get; }

        public DocumentGenerationParticipantResponse(string name, string email, string accessUri, string role)
        {
            this.Name = name;
            this.Email = email;
            this.AccessUri = accessUri;
            this.Role = role;
        }

        public override bool Equals(object obj)
        {
            if (obj is null)
            {
                return false;
            }

            if (ReferenceEquals(this, obj))
            {
                return true;
            }

            DocumentGenerationParticipantResponse tmp = (DocumentGenerationParticipantResponse)obj;
            return Equals(this.Name, tmp.Name) &&
                   Equals(this.Email, tmp.Email) &&
                   Equals(this.AccessUri, tmp.AccessUri) &&
                   Equals(this.Role, tmp.Role);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = -2099;
                hashCode = hashCode * -2399 + EqualityComparer<string>.Default.GetHashCode(Name);
                hashCode = hashCode * -2399 + EqualityComparer<string>.Default.GetHashCode(Email);
                hashCode = hashCode * -2399 + EqualityComparer<string>.Default.GetHashCode(AccessUri);
                hashCode = hashCode * -2399 + EqualityComparer<string>.Default.GetHashCode(Role);
                return hashCode;
            }
        }
    }
}

DocumentGenerationParticipantsResponse:

using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp4.DocumentGeneration.DocumentGenerationParticipant
{
    public class DocumentGenerationParticipantsResponse
    {
        public List<DocumentGenerationParticipantResponse> Participants { get; }

        public DocumentGenerationParticipantsResponse(List<DocumentGenerationParticipantResponse> participants)
        {
            Participants = participants;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            if (!(obj is List<DocumentGenerationParticipantResponse> list))
            {
                return false;
            }

            if (list.Count != this.Participants.Count)
            {
                return false;
            }

            return this.Participants.SequenceEqual(list);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = -2099;
                this.Participants.ForEach(x => hashCode = hashCode * x.GetHashCode());
                return hashCode;
            }
        }
    }
}
  

错误消息:“消息”:“发生错误。”,       “ ExceptionMessage”:“为类型的属性'参与者'检测到自引用循环   “ System.Collections.Generic.List`1 [XXXXXX.Api.Management.Contracts.DocumentGeneration.DocumentGenerationParticipantResponse]”。   路径“。”,

1 个答案:

答案 0 :(得分:1)

Your problem is that you overrode DocumentGenerationParticipantsResponse.Equals(object obj) to make an object of type DocumentGenerationParticipantsResponse equal to one of its own properties:

public class DocumentGenerationParticipantsResponse
{
    public List<DocumentGenerationParticipantResponse> Participants { get; }

    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }

        if (!(obj is List<DocumentGenerationParticipantResponse> list))
        {
            return false;
        }

        if (list.Count != this.Participants.Count)
        {
            return false;
        }

        return this.Participants.SequenceEqual(list);
    }

    // Remainder snipped

Specifically it will be equal to any List<DocumentGenerationParticipantResponse> with the same contents as its own Participants list. This explains the exception you are seeing. As explained in this answer to Why doesn't reference loop detection use reference equality?, Json.NET uses object equality rather than reference equality in checking for circular references. Thus, when serializing the Participants property, Json.NET thinks it is equal to the parent DocumentGenerationParticipantsResponse, and throws the exception you are seeing.

If you don't want to use object equality when checking for circular references, you can override JsonSerializerSettings.EqualityComparer and replace the default behavior of calling object.Equals in reference loop detection with something else, e.g. ReferenceEqualityComparer from this answer to IEqualityComparer<T> that uses ReferenceEquals by AnorZaken. But before doing that, you should examine your Equals() methods to make sure they are really doing what you want, since in this case DocumentGenerationParticipantsResponse.Equals() looks broken as the equality method is returning true for objects of different type.

(And, as noted in this answer by Daniel Gimenez, DocumentGenerationParticipantResponse.Equals() also looks broken. Among other issues it does not check for the incoming object obj being of the correct type.)

Further reading: