当前位置: 首页 > news >正文

C# 序列化时“检测到循环引用”错误的彻底解决方案

目录

一,问题表现 

二、没有技术含量的解决方案 

三、本人彻底的解决方案

简要说明

贴代码

思路解析

思路


一,问题表现 

示例代码如下:

[Serializable]
public class NodeTest 
{public NodeTest (){new List<NodeTest> ();}public string Name { get; set; }public NodeTest Parent { get; set; }public List<NodeTest> Children { get; set; }}

先看错误地方,以上这个类要是序列化就会遇到,"序列化类型 Test.NodeTest 的对象时检测到循环引用。"错误。

二、没有技术含量的解决方案 

网上一搜,几乎到处都是这两种解决方案:

  1. 使用NewtonSoft.Json,然后使用序列方法,加上设置 ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
  2. 直接在循环错误属性上加XmlIgnore特性。
                NodeTest nd = new NodeTest ();nd.Name = "root";NodeTest nd1 = new NodeTest ();nd1.Name = "child1";nd1.Parent = nd;NodeTest nd2 = new NodeTest ();nd2.Name = "child2";nd2.Parent = nd;nd.Children.Add ( nd1 );nd.Children.Add ( nd2 );

上面的实例,采用第一种方法序列化是这结果:

 采用第二种是以下结果。

由此可见这两种方法简单,粗暴,没有一点技术含量。这么说是因为,直接忽略了其父子关系。反序列化成对象后Parent属性为空,如果需要逆向查找父对象时,完全行不通。

三、本人彻底的解决方案

  • 简要说明

首先将例中NodeTest对象进行改装,让它继承自 IXmlSerializable 接口并实现,为的就是在序列化和反序列化时,可以自由控制以达到序列化时能包含父节点信息。其次是对他的属性Children进行改造,这很重要,如果继续使用List列表,会出现其他问题,这个后续会提到。

  • 贴代码

现不废话,贴代码,代码看完,看后文解析,应该很容易明白,NodeTest 类:

    [Serializable]public class NodeTest : IXmlSerializable{internal const string ROOT = "NodeTest";internal const string NAME = "Name";internal const string PARENT = "Parent";internal const string ELEMENT_EXISTMARK = "ExistMark";internal const string ELEMENT_EXIST = "Exist";internal const string ELEMENT_NOTEXIST = "NotExist";public NodeTest (){Children = new NodeTestCollection ();}public string Name { get; set; }public NodeTest Parent { get; set; }[XmlArray ()]public NodeTestCollection/*List<NodeTest>*/ Children { get; set; }public System.Xml.Schema.XmlSchema GetSchema (){return null;}public static void ResetSerializationStatus (){_dicAllNodes.Clear ();}private static Dictionary<string, NodeTest> _dicAllNodes= new Dictionary<string, NodeTest> ();public void ReadXml ( System.Xml.XmlReader reader ){XmlSerializer xmlSer = XmlSerializer.FromTypes ( new Type[ ] { typeof ( NodeTest ) } )[ 0 ];if ( reader.IsEmptyElement ) return;while ( reader.NodeType != System.Xml.XmlNodeType.EndElement ){reader.ReadStartElement ( ROOT );reader.ReadStartElement ( NAME );this.Name = reader.ReadString ();reader.ReadEndElement ();reader.MoveToContent ();string sExistMark = ELEMENT_NOTEXIST;if ( reader.MoveToAttribute ( ELEMENT_EXISTMARK ) ){sExistMark = reader.GetAttribute ( ELEMENT_EXISTMARK );}reader.ReadStartElement ( PARENT );switch ( sExistMark ){case ELEMENT_EXIST:reader.ReadStartElement ( NAME );Parent = new NodeTest ();Parent.Name = reader.ReadString ();reader.ReadEndElement ();reader.ReadEndElement ();break;default:break;}XmlSerializer xmlSer2 = XmlSerializer.FromTypes ( new Type[ ] { typeof ( NodeTestCollection ) } )[ 0 ];Children = ( NodeTestCollection )xmlSer2.Deserialize ( reader );if ( Children.Count != 0 ){reader.ReadEndElement ();}_dicAllNodes.Add ( this.Name, this );}for ( int i = 0 ; i < Children.Count ; i++ ){var child = Children[ i ];if ( child.Parent != null ){child.Parent = _dicAllNodes[ child.Parent.Name ];}}}public void WriteXml ( System.Xml.XmlWriter writer ){XmlSerializer xmlSer = XmlSerializer.FromTypes ( new Type[ ] { typeof ( NodeTest ) } )[ 0 ];writer.WriteStartElement ( NAME );writer.WriteString ( this.Name );writer.WriteEndElement ();writer.WriteStartElement ( PARENT );if ( Parent != null ){writer.WriteAttributeString ( ELEMENT_EXISTMARK, ELEMENT_EXIST );writer.WriteStartElement ( NAME );writer.WriteString ( Parent.Name );writer.WriteEndElement ();}else{writer.WriteAttributeString ( ELEMENT_EXISTMARK, ELEMENT_NOTEXIST );}writer.WriteEndElement ();writer.Flush ();XmlSerializer xmlSer2 = XmlSerializer.FromTypes ( new Type[ ] { typeof ( NodeTestCollection ) } )[ 0 ];xmlSer2.Serialize ( writer, this.Children );writer.Flush ();writer.Flush ();}}

List<NodeTest>改成NodeTestCollection,解决序列化时结构错乱问题(本来打算细说的,算了,懒得打字了,看客自己试试就知道了),此改动,还很利于后续的优化扩展,比如名字索引等,实现如下:

    [Serializable][XmlRoot(ElementName = "Children" )]public class NodeTestCollection : IList<NodeTest>, IXmlSerializable{private const string ROOT = "Children";private const string CHILDCOUNT = "ChildCount";#region 继承实现自IList<NodeTest>private IList<NodeTest> m_lstNodes = new List<NodeTest> ();public int IndexOf ( NodeTest item ){int iIdx = -1;for ( int i = 0 ; i < m_lstNodes.Count ; i++ ){if ( m_lstNodes[ i ] == item ){iIdx = i;break;}}return iIdx;}public void Insert ( int index, NodeTest item ){m_lstNodes.Insert ( index, item );}[XmlElement ( "NodeTest", Type = typeof ( NodeTest ) )]public NodeTest this[ int index ]{get{return m_lstNodes[ index ];}set{m_lstNodes[ index ] = value;}}public void Add ( NodeTest item ){m_lstNodes.Add ( item );}public bool Contains ( NodeTest item ){return m_lstNodes.Contains ( item );}public void CopyTo ( NodeTest[ ] array, int arrayIndex ){m_lstNodes.CopyTo ( array, arrayIndex );}public bool Remove ( NodeTest item ){return m_lstNodes.Remove ( item );}IEnumerator<NodeTest> IEnumerable<NodeTest>.GetEnumerator (){return m_lstNodes.GetEnumerator ();}public void RemoveAt ( int index ){m_lstNodes.RemoveAt ( index );}public void Clear (){m_lstNodes.Clear ();}public int Count{get { return m_lstNodes.Count; }}public bool IsReadOnly{get { return false; }}System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator (){return m_lstNodes.GetEnumerator ();}#endregionpublic System.Xml.Schema.XmlSchema GetSchema (){return null;}public void ReadXml ( System.Xml.XmlReader reader ){//if ( reader.IsEmptyElement ) return;int iChildCount = 0;if ( reader.MoveToAttribute ( CHILDCOUNT ) ) iChildCount = int.Parse ( reader.GetAttribute ( CHILDCOUNT ) );reader.ReadStartElement ( ROOT );if (iChildCount > 0){XmlSerializer xmlSer = XmlSerializer.FromTypes ( new Type[ ] { typeof ( NodeTest ) } )[ 0 ];for ( int i = 0 ; i < iChildCount ; i++ ){var readerSub = reader.ReadSubtree();this.Add ( ( NodeTest )xmlSer.Deserialize ( readerSub ) );reader.ReadEndElement ();}}}public void WriteXml ( System.Xml.XmlWriter writer ){int iCnt = this.Count;writer.WriteAttributeString ( CHILDCOUNT, iCnt.ToString () );XmlSerializer xmlSer = XmlSerializer.FromTypes ( new Type[ ] { typeof ( NodeTest ) } )[ 0 ];for ( int i = 0 ; i < iCnt ; i++ ){xmlSer.Serialize ( writer, this[ i ] );}}}
  • 思路解析

说说思路:改造的地方不多,主要是继承方法的实现上,也就是ReadXml WriteXml方法实现上。在实现它们时,采用一点手段,在方法体类对循环引用的对象进行区别处理。

思路

原理很简单,循环引用的地方,在本例中即Parent对象,采用简单存储,存储一个指引信息,这里为图简洁,直接使用Name属性作为指引【后续各位观众具体使用是可以使用唯一标识符进行优化,在此我就不改了】。将所有NodeTest对象信息存为字典,在反序列化时使用指引进行挂接,因为这是class,是引用对象,简单一改,全部挂接完成,挂接处就是For循环处。

此外还有两处XmlAttribute——ExistMark和ChildCount,分别用来辅助Parent存在时检测以及子节点存在检测。

采用这个方法,序列化出来如下图:

 

反序列化也成功进行挂接,不信,你试试。

原理就这么简单。

 不懂就留言吧。

相关文章:

C# 序列化时“检测到循环引用”错误的彻底解决方案

目录 一&#xff0c;问题表现 二、没有技术含量的解决方案 三、本人彻底的解决方案 简要说明 贴代码 思路解析 思路 一&#xff0c;问题表现 示例代码如下&#xff1a; [Serializable] public class NodeTest {public NodeTest (){new List<NodeTest> ();}p…...

小红书“复刻”微信,微信“内造”小红书

配图来自Canva可画 随着互联网增长红利逐渐见顶&#xff0c;各大互联网平台对流量的争夺变得愈发激烈。而为了寻找新的业务可能性&#xff0c;各家都在不遗余力地拓宽自身边界。在此背景下&#xff0c;目前最为“吸睛”和“吸金”的社交、电商、种草、短视频等领域&#xff0c…...

用arthas轻松排查线上问题

你是否在项目中会碰到以下一些问题&#xff1a; 在代码中打印各种日志来排查&#xff0c;比如方法的入参&#xff0c;出参&#xff0c;及在方法体中打印日志判断走哪行代码还有你觉得代码没问题&#xff0c;可是运行出现却是以前的bug&#xff0c;感觉代码没修改&#xff0c;或…...

mysql一explain结果分析

1. EXPLAIN简介 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句&#xff0c;从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。 ➤ 通过EXPLAIN&#xff0c;我们可以分析出以下结果&#xff1a; 表的读取顺序数据读取操作的操作类型哪些索引可…...

原理底层计划--HashMap

HashMap 之前写了“Java集合TreeMap红黑树一生只爱一次”&#xff0c;说到底还是太年轻了&#xff0c;Map其实在排序中应用比较少&#xff0c;一般追求的是速度&#xff0c;通过HashMap来获取速度。hashmap 调用object hashcode方法用于返回对象的哈希码&#xff0c;主要使用在…...

win10 设备管理器中的黄色感叹号(华硕)

目录一、前言二、原因三、方案四、操作一、前言 打开设备管理器&#xff0c;我们可以看到自己设备的信息&#xff0c;但是在重装系统后&#xff0c;你总会在不经意间发现。咦&#xff0c;怎么多了几个感叹号&#xff1f;&#xff1f;&#xff1f; 由于我已经解决该问题&#…...

新产品上市推广不是“铺货”上架

只有不断推出新产品的企业才能走得长远&#xff0c;但现实中往往有很多企业投入了大量人力、物力、财力研发的新产品却在推广的过程中屡屡受挫。那么&#xff0c;为什么适合市场的新产品会在市场营销推广的过程中夭折呢&#xff1f;小马识途营销顾问分析有如下几点&#xff1a;…...

MATLAB训练神经网络小结

MATLAB训练神经网络小结1、一个典型例子1.1 可视化神经网络1.2 指定某一层的激活函数1.3 训练神经网络时使用L1正则化1.4返回训练过程中的参数1.5 查看训练好的权重系数1.6 如何使用早停法来防止过拟合1、一个典型例子 例如输入特征为10维&#xff0c;想训练一个10x20x10x1的三…...

实战:一天开发一款内置游戏直播的国产版Discord应用【附源码】

游戏直播是Discord产品的核心功能之一&#xff0c;本教程教大家如何1天内开发一款内置游戏直播的国产版Discord应用&#xff0c;用户不仅可以通过IM聊天&#xff0c;也可以进行语聊&#xff0c;看游戏直播&#xff0c;甚至自己进行游戏直播&#xff0c;无任何实时音视频底层技术…...

嵌入式学习笔记——基于Cortex-M的单片机介绍

基于Cortex-M的单片机介绍前言生产厂商及其产品线ARM单片机的产品线命名规则留个作业习单片机的资料准备STM32开发所需手册1.芯片的数据手册作业2前言 本文继续接着上一篇中关于Cortex-M的介绍&#xff0c;来记录一些关于ARM系单片机的知识。 生产厂商及其产品线 芯片厂商在…...

Python 虚拟环境的使用

PyCharm 创建的虚拟环境与使用 workon 命令创建的虚拟环境在本质上没有区别&#xff0c;它们都是 Python 的虚拟环境。 使用 PyCharm 创建工程时&#xff0c;使用可以使用曾经工程的虚拟环境&#xff0c;或者新建一个虚拟环境来安装 Python 的库&#xff0c;又或者使用 workon…...

招生咨询|浙江大学MPA项目2023年招生问答与通知

问&#xff1a;报考浙江大学MPA的基本流程是怎么样的&#xff1f; 答&#xff1a;第一阶段为网上报名与确认。MPA考生须参加全国管理类联考&#xff0c;网上报名时间一般为10月初开始、10月下旬截止&#xff0c;错过网上报名时间后不能补报。确认时间一般为11月上旬&#xff0c…...

Qt std :: bad_alloc

文章目录摘要问题出现原因第一种 请求内存多余系统可提供内存第二种 地址空间过于分散&#xff0c;无法满足大块连续内存的请求第三种 堆管理数据结构损坏稍微总结下没想到还能更新参考关键字&#xff1a; std、 bad、 alloc、 OOM、 异常退出摘要 今天又是被BUG统治的一天&a…...

《设计模式》装饰者模式

《设计模式》装饰者模式 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许在不改变现有对象结构的情况下&#xff0c;动态地添加行为或责任到对象上。在装饰者模式中&#xff0c;有一个抽象组件&#xff08;Component&#xff09;…...

一文说清Kubernetes的本质

文章目录Kubernetes解决了什么问题&#xff1f;Kubernetes的全局架构Kubernetes的设计思想Kubernetes的核心功能Kubernetes如何启动一个容器化任务&#xff1f;Kubernetes解决了什么问题&#xff1f; 编排&#xff1f;调度&#xff1f;容器云&#xff1f;还是集群管理&#xf…...

信息发布小程序【源码好优多】

简介 信息发布小程序&#xff0c;实现数据与小程序数据同步共享&#xff0c;通过简单的配置就能搭建自己的小程序。&#xff0c;基于微信小程序开发的小程序。 这个框架比较简单就是用微信原生开发技术进行实现的&#xff0c;可以用于信息展示等相关信息。其中目前APP比较多&am…...

创新型中小企业申报流程

据工业和信息化部《优质中小企业梯度培育管理暂行办法》&#xff08;工信部企业〔2022〕63号&#xff09;和省《优质中小企业梯度培育管理实施细则》&#xff08;鲁工信发〔2022〕8号&#xff0c;以下简称《细则》&#xff09;&#xff0c;现就做好2022年山东省创新型中小企业评…...

【UE4 Cesium】加载离线地图

主体思路&#xff1a;先使用水经注软件下载瓦片数据&#xff0c;再使用Python转换瓦片数据格式&#xff08;TMS&#xff09;&#xff0c;使用Nginx发布网络服务&#xff0c;最后将网络服务加载到UE中。步骤&#xff1a;使用水经注下载瓦片数据&#xff0c;这里下载的是全球七级…...

Spring面试题

目录 Spring、Springmvc、Springboot的区别是什么 SpringMVC工作流程是什么 SpringMVC的九大组件有哪些 Spring的核心是什么 spring的事务传播机制是什么 Spring框架中的单例Bean是线程安全的么 spring框架中使用了哪些设计模式及应用场景 spring事务的隔离级别有哪些?…...

动态网站开发讲课笔记03:HTTP协议

文章目录零、本节学习目标一、HTTP概述&#xff08;一&#xff09;HTTP的概念1、HTTP的概念2、HTTP协议的特点&#xff08;1&#xff09;C/S模式&#xff08;2&#xff09;简单快速&#xff08;3&#xff09;灵活&#xff08;4&#xff09;无状态&#xff08;二&#xff09;HTT…...

2023年天津财经大学珠江学院专升本专业课考试题型

天津财经大学珠江学院关于2023年高职升本科专业课考试时间及题型一、专业课考试 &#xff08;一&#xff09;时间安排 2023年天津财经大学珠江学院高职升本科专业课考试定于2023年3月25日14&#xff1a;00-17:00进行&#xff0c;凡报考工商管理、旅游管理、税收学专业的考生&am…...

五方面提高销售流程管理的CRM系统

销售充满了不确定性&#xff0c;面对不同的客户&#xff0c;销售人员需要采用不同的销售策略。也正因为这种不确定性&#xff0c;规范的销售流程对企业尤为重要&#xff0c;它会让销售工作更加有效&#xff0c;快速地实现成交。下面小编给您推荐个不错的CRM销售流程管理系统。 …...

AutoCAD通过handle id选择实体

获得实体的handle id。注意是handle id 不是id&#xff0c;方法有2种&#xff1a;方法&#xff08;a&#xff09;&#xff1a;通过ArxDeg插件&#xff08;ObjectARX附带的源码编译得到&#xff1a;\samples\database\ARXDBG&#xff09;查找&#xff1a;此handle id本来就是16进…...

页面状态码的含义

使用互联网产品或服务的过程中&#xff0c;会遇到网页报错的情况&#xff0c; 比如404、505等&#xff0c;具体这些数字有什么含义呢&#xff1f;本文基本涵盖了99%的报错情况&#xff0c;可供大家查询使用。 状态码的定义 状态码一般是由3位数字和原因短语组成的&#xff08…...

Redis 越来越慢?常见延迟问题定位与分析

Redis作为内存数据库&#xff0c;拥有非常高的性能&#xff0c;单个实例的QPS能够达到10W左右。但我们在使用Redis时&#xff0c;经常时不时会出现访问延迟很大的情况&#xff0c;如果你不知道Redis的内部实现原理&#xff0c;在排查问题时就会一头雾水。很多时候&#xff0c;R…...

【python】python-socketio+firecamp使用踩坑指南

server.py: import eventlet import asyncioeventlet.monkey_patch()import socketio import eventlet.wsgisio socketio.Server(async_modeeventlet, cors_allowed_origins*) # 指明在evenlet模式下sio.event def connect(sid, environ):print(f"connect, sid{sid}, e…...

【OJ比赛日历】快周末了,不来一场比赛吗? #03.04-03.10 #12场

CompHub 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号同时会推送最新的比赛消息&#xff0c;欢迎关注&#xff01;更多比赛信息见 CompHub主页 或 点击文末阅读原文以下信息仅供参考&#xff0c;以比赛官网为准目录2023-03-04&…...

C++11:继承

目录 继承的基本概念 继承方式 基类和派生类对象赋值转换/切片 继承中的作用域 派生类的四个成员函数&#xff1a; 构造函数 拷贝构造函数 赋值重载 析构函数 静态成员 继承与友元 多继承 菱形继承 多继承的指针偏移问题 组合 继承的基本概念 继承出现的契机是某一…...

【蓝桥杯试题】递归实现排列型枚举

&#x1f483;&#x1f3fc; 本人简介&#xff1a;男 &#x1f476;&#x1f3fc; 年龄&#xff1a;18 &#x1f91e; 作者&#xff1a;那就叫我亮亮叭 &#x1f4d5; 专栏&#xff1a;蓝桥杯试题 文章目录1. 题目描述2. 代码展示法一&#xff1a;dfs法二&#xff1a;next_perm…...

入职字节测试岗外包一个月,我离职了...

有一种打工人的羡慕&#xff0c;叫做“大厂”。真是年少不知大厂香&#xff0c;错把青春插稻秧。但是&#xff0c;在深圳有一群比大厂员工更庞大的群体&#xff0c;他们顶着大厂的“名”&#xff0c;做着大厂的工作&#xff0c;还可以享受大厂的伙食&#xff0c;却没有大厂的“…...

wordpress文章列表不显示/五种网络营销推广方法

我在使用地税发票控件进行开票的测试的时候&#xff0c;在xp上测试时正常的&#xff0c;在别人的win7系统测试也是正常&#xff0c;但我在我本机确不正常。我本机装的是msdn版本win7系统&#xff0c;这个系统比较原装。 错误信息如下&#xff1a; 2012-07-21 06:30:08,546 [1] …...

做网站哪个公司最好/搜狗推广

少人做的水题。直接模拟即可&#xff0c;不算dp题。 代码&#xff1a; #include<iostream> #include<fstream>using namespace std;int a[10001][7]; int n;int ok(int s,int t){int i0;if(t1){imax(a[s][2],i);imax(a[s][3],i);imax(a[s][4],i);imax(a[s][5],i);}…...

石景山网站建设制作公司/买友情链接有用吗

git add . &#xff1a;他会监控工作区的状态树&#xff0c;使用它会把工作时的所有变化提交到暂存区&#xff0c;包括文件内容修改(modified)以及新文件(new)&#xff0c;但不包括被删除的文件。 git add -u &#xff1a;他仅监控已经被add的文件&#xff08;即tracked file&a…...

门户网站的注意要素/西安百度推广开户运营

JAVA BaseRepository类下封装了一些方法。 1 新增 int insert(T record) : 插入一条记录&#xff1b; int insertSelective(T record) &#xff1a; 插入一条记录&#xff0c;Bean中null的字段不会被插入&#xff1b; int insertOptional(T record) : 插入一条记录&#xff0c;…...

php网站平台/龙岗网站推广

赞助商链接本文“PHP Data Object 新的数据库衔接方法[网站编程]”是由七道奇为您精心收集&#xff0c;来源于网络转载&#xff0c;文章版权归文章作者所有&#xff0c;本站不对其观点以及内容做任何评价&#xff0c;请读者自行判断&#xff0c;以下是其具体内容&#xff1a;P…...

玄武模板网站制作品牌/网络营销师官网

其实循环生成css都是定义一个数组&#xff0c;然后遍历生成&#xff0c;主要在于各个语言的调用方式不一样. 这里使用的rem作为基准单位&#xff0c;也可以用px或者其他 _variable.scss文件 // 文件名以下划线开头表示被引用文件&#xff0c;引用时不用加下划线和后缀名//定义…...