使用自定义Equals和GetHashCode方法序列化对象时的循环引用错误

时间:2011-12-22 21:31:35

标签: .net xml web-services serialization xml-serialization

我得到在我的asmx web服务的WebMethod返回我的对象​​时,在序列化类型为T 的对象时检测到循环引用。

如果我从T类中删除Equals和GetHashCode问题就消失了。 我没有任何循环引用,所以看起来序列化通过比较对象检测循环引用,如果它们相等则认为有圆圈。

当然,我可以使用Equals定义一个类,并像许多人一样定义另一个类进行序列化,然后将数据从一个复制到另一个类,但我希望能够在一个类中执行它以避免{{3 } parallel class hierarchies之一。

我希望能够定义Equals,GetHashCode并保持code smells。怎么样?

3 个答案:

答案 0 :(得分:1)

让我深入了解麻烦的根源。 因为我有同样的问题。

我意识到,问题是在GetHashCode方法中找不到的,而是在Equals方法中找到。

XmlSerializer抛出异常的最重要原因是因为XML文档的结构和"等同机制"在XmlSerializer内部。

例如:

private XmlSerializer serializer;

public void TryToSerialize(TextWriter output)
{
    MyObject instance = new MyObject();
    instance.Key = 101;
    instance.SomeValue = "Some value";
    instance.Child = new MyObject();
    instance.Child.Key = 101;
    instance.Child.SomeValue = "Another value";

    serializer.Serialize(output, instance);
}

我是如何实现GetHashCode方法和Equals方法的?

像这样:

public overrides int GetHashCode()
{
    return this.Key.GetHashCode();
}

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

    MyObject other = obj as MyObject;
    if(other == null) return false;

    return this.Key.Equals(other.Key);
}

如果我运行" TryToSerialize" -method,会发生什么? 我将收到 InvalidOperationException ,并显示消息在序列化类型为T 的对象时检测到循环引用。

在血清化时,XmlSerializer会尝试避免将与子项相同的对象添加到XML文档中,因为这会导致循环。 但检查"的方式是同一个对象"是使用GetHashCode方法和Equals方法来实现它们的目的 - 检查对象的相等性。

在我们的示例中,这些对象migth是不同的实例,并且XmlSerializer不检查内存中的"实例",但使用它知道的方法 - GetHashCode和Equals。

因此,请考虑如何实施 Equals-method

或更好......

考虑如何改进类和方法的实现,以避免在麻烦的根源中出现此问题。 ; - )

答案 1 :(得分:0)

一种方法是从一个接口继承序列化中涉及的每个类,该接口定义OnSerializing方法并在每个类中实现该方法以为符合条件的子级调用它。在每个类中都有Seri​​alizing private bool成员,默认为false,并在OnSerializing中将其设置为true。这将允许在root上调用它并传播到所有可序列化节点,直到每个叶子。

在从WebMethod返回可序列化的returnValue之前调用returnValue.OnSerializing()。

每次覆盖等于第一行应为if(Serializing)return base.Equals(obj);,GetHashCode中的第一行应为if(Serializing)return base.GetHashCode();

通过调用OnSerializing从WebMethod返回之前,就是标记整个树以禁用Equals和GetHashCode的自定义。

如果您需要在序列化后执行某些操作(例如,保存在磁盘上然后返回),则定义OnSerialized并通过树传播,以将Serializing标志设置为false。

当然,如果可能,只需从一个类而不是接口继承所有可序列化对象,并在那里实现所有内容,以减少每个可序列化类中的实现开销。

答案 2 :(得分:0)

就我而言,我解决了正确实现Equals和GetHashCode方法的问题(自动Visual Studio代码补全),如下所示:

import React from 'react';
import {
    configure,
    shallow
} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

import MenuList from './MenuList';
import {
    Link
} from 'react-router-dom';

configure({
    adapter: new Adapter()
});

const footerItem = {
    label: '',
    links: [{
        label: '',
        link: '/'
    }]
}

describe('<MenuList />', () => {

            let wrapper;
            beforeEach(() => {
                    wrapper = shallow( < MenuList footerItem = {
                            footerItem
                        }
                        / > );
                    });

                it('should render Link', () => {
                    console.log(wrapper.props().footerItem); // This should print footerItem object but returning  undefined
                    expect(wrapper.find(Link).length).toBeGreaterThan(0);
                });
            });

// and here is the code of menulist component/


import React from 'react';
import { Link } from 'react-router-dom';

const menuList = (props) => {
    let linkArray=[];
        linkArray = props.footerItem.links.map((item,index)=>{
            return <li key={index}>
                    <Link to={item.link}>
                        {item.label}
                    </Link></li>
        })

   return (
        <div className="footer-link">
            <h6>{props.footerItem.label}</h6>
                <ul>
                    {linkArray}
                </ul>
        </div>
   )
}
export default menuList;

以前的代码只是Equals方法,就像这样:

 public override bool Equals(object obj)
        {
            var permission = obj as Permission;
            return permission != null &&
                   EqualityComparer<AccessLevel>.Default.Equals(Access, permission.Access) &&
                   EqualityComparer<Functionality>.Default.Equals(Function, permission.Function);
        }

        public override int GetHashCode()
        {
            var hashCode = -720887508;
            hashCode = hashCode * -1521134295 + EqualityComparer<AccessLevel>.Default.GetHashCode(Access);
            hashCode = hashCode * -1521134295 + EqualityComparer<Functionality>.Default.GetHashCode(Function);
            return hashCode;
        }