问题描述
JSON.NET加载json字符串时,如何自定义映射到某个属性?例如,原来写JSON时属性叫“Name",现在叫”NewName“,如何把旧的名称,映射到新的属性上?有没有类似IXmlSerializable的接口,可以自定义读写过程?
解决方案
解决方案二:
staticclassProgram{classMy{[JsonProperty("Name")]//用JsonProperty特性publicstringNewName{get;set;}}staticvoidMain(){stringjson="{'Name':'JamesBond'}";Mymy=JsonConvert.DeserializeObject<My>(json);//my.NewName:"JamesBond"}}
解决方案三:
引用1楼Forty2的回复:
staticclassProgram{classMy{[JsonProperty("Name")]//用JsonProperty特性publicstringNewName{get;set;}}staticvoidMain(){stringjson="{'Name':'JamesBond'}";Mymy=JsonConvert.DeserializeObject<My>(json);//my.NewName:"JamesBond"}}
这样确实是可以解决掉读取旧名称问题,问题是,再写JSON时,还是要用“Name”写,而不是“NewName”啊?我的本意是要解决数据文件的升级,同时兼容旧文件(属性名称)。
解决方案四:
json.net可以通过attribute来实现
解决方案五:
classMy{[JsonProperty(PropertyName="Name",DefaultValueHandling=DefaultValueHandling.Ignore)]privatestring_name_for_old_contract{get{returnnull;}set{NewName=value;}}[JsonProperty]publicstringNewName{get;set;}}
解决方案六:
新旧名字问题没办法,读出来后你可以通过automapper再映射到最新的实体
解决方案七:
直接映射不行,你要先重新定义一个json结构体,然后把新旧JSON合并
解决方案八:
引用4楼Forty2的回复:
classMy{[JsonProperty(PropertyName="Name",DefaultValueHandling=DefaultValueHandling.Ignore)]privatestring_name_for_old_contract{get{returnnull;}set{NewName=value;}}[JsonProperty]publicstringNewName{get;set;}}
引用6楼lshfong的回复:
直接映射不行,你要先重新定义一个json结构体,然后把新旧JSON合并
多谢2位,谢谢你们的思路。不过,感觉这样有一个弊端,就是如果每次改1-2个属性,必须保留整个的旧实体类在代码中。有没有办法,可以反序列化时,由代码介入,同时,可分级处理,例如:classA{intIDClassBBDeserialize(){//A类反序列化自己的部分,B类部分,交由B类反序列化B.Deserialize(ContextObjectcontext)}}classClassB{Deserialize(){//B类反序列化自己}}
解决方案九:
兼容性是永远存在的问题,不论你用什么方式来“处理”,总之都抹不掉痕迹。但是你可以用“倒置”的方式来重构你的系统。例如原来有一个class包厢{publicstring门牌号;publicint价格;...................}
后来改为public包厢{publicstring包厢号;publicint价格;..............}
假设此时不允许暴露“门牌号”字段,那么你需要为以前的数据单独设计一个兼容类型,例如internal包厢V1版:包厢{publicstring门牌号{get{returnthis.包厢号;}set{this.包厢号=value;}}}
并且在可以确定是新数据上使用“包厢”,在不确定只有新数据时使用“包厢V1版”。等将来再删除包厢V1版,或者永远也不删除。但是新系统,对外已经不提供包厢V1版的类型定义了。
解决方案十:
引用7楼oldhunter的回复:
不过,感觉这样有一个弊端,就是如果每次改1-2个属性,必须保留整个的旧实体类在代码中。
那说明你没有什么抽象层设计,都是把原本最底层的东西放到最顶层接口来编写代码。
解决方案十一:
引用8楼sp1234的回复:
兼容性是永远存在的问题,不论你用什么方式来“处理”,总之都抹不掉痕迹。但是你可以用“倒置”的方式来重构你的系统。例如原来有一个class包厢{publicstring门牌号;publicint价格;...................}后来改为public包厢{publicstring包厢号;publicint价格;..............}
假设此时不允许暴露“门牌号”字段,那么你需要为以前的数据单独设计一个兼容类型,例如internal包厢V1版:包厢{publicstring门牌号{get{returnthis.包厢号;}set{this.包厢号=value;}}}
并且在可以确定是新数据上使用“包厢”,在不确定只有新数据时使用“包厢V1版”。等将来再删除包厢V1版,或者永远也不删除。但是新系统,对外已经不提供包厢V1版的类型定义了。
多谢多谢!思路很完美!延伸2个技术层面的问题:1、解析JSON字符串时,如何判断数据版本号?要写一个属性吗?还是JSON.NET有现成的Atrribute?2、如果包含的”引用类型“升级后,如何把解析分配给”旧版本“的引用类型?如下所示:序列化JSON是以“火车”为单位处理的,而“火车”又包含了“包厢”,”包厢“类升级后,如何将数据解析到”包厢V1版“?用JSON.NET如何处理?因为JSON.NET正常是解析到”包厢“类。(JSON.NET即:Newtonsoft.Json)class火车{publicstring名称;public包厢所属包厢;}class包厢{publicstring门牌号;publicint价格;...................}internal包厢V1版:包厢{publicstring门牌号{get{returnthis.包厢号;}set{this.包厢号=value;}}}
解决方案十二:
感觉你们讨论是没有意义的楼主最初的问题应该是兼容不同来源json但你们的讨论中,是以已知差异的具象为前提的:差异不同,代码也不同而事实是:你根本就不会知道你明天会有什么新想法也就是你不可能预知未来的差异会在哪里和是什么如果出现差异就修改程序,显然就离开了问题的初衷
解决方案十三:
引用11楼xuzuning的回复:
感觉你们讨论是没有意义的楼主最初的问题应该是兼容不同来源json但你们的讨论中,是以已知差异的具象为前提的:差异不同,代码也不同而事实是:你根本就不会知道你明天会有什么新想法也就是你不可能预知未来的差异会在哪里和是什么如果出现差异就修改程序,显然就离开了问题的初衷
可以基于一些范围假设:1、未来数据基于JSON2、属性名称重构后,可兼容旧属性,同时满足代码的可维护性、封装性(个人觉得,用“从新版本继承一个实体类,解析旧属性”的解决方案就不错,即sp1234的思路)3、增加新属性:只能使用默认值,或基于旧属性转换(从新版本继承一个实体类,解析旧属性)4、引用子类的类名、属性重构(需要讨论:如何让json在解析时,自定义解析流程,即根据版本号解析到不同的类。其实主类好办,引用子类如何选择?JSON.NET类库是否提供了这方面的扩展,如Newtonsoft.Json.Serialization.DefaultContractResolver)5、类引用关联重构、业务逻辑重构:这个范围就太大了,不好控制,有可能基于“旧类”解析已经没有意义,无法兼容。
解决方案十四:
如何让json在解析时,自定义解析流程,即根据版本号解析到不同的类。其实主类好办,引用子类如何选择?JSON.NET类库是否提供了这方面的扩展,如Newtonsoft.Json.Serialization.DefaultContractResolver
解决方案十五:
我觉得你有空讨论这个,还不如自己实现一个json,又不是很复杂的工作