XML序列化与反序列化接口对接实战,看这篇就够了
关键字:c# .NET XML 序列化 反序列化
本文为接口对接实践经验分享,不对具体的XML概念定义进行阐述;涉及工具类及处理方法已在生产环境使用多年,可放心使用。当然如果你发现问题,或有不同想法,也非常欢迎指出讨论。
以系统对接为基础,本文力求达到:
- 工具类直接使用;
- XML处理示例全覆盖;
- 让有XML格式需求的朋友看这篇就够了;
JSON同样作为接口数据格式最常用选择,计划另写一篇。敬请关注
0、工具类
点击查看 XmlParser 工具类
public static class XmlParser<T>
{
private static XmlSerializer fXmlSerializer = null;
private static UTF8Encoding fUtf8 = new UTF8Encoding();
/// <summary>
/// 带头信息;无命名空间
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public static string ToXml(T item)
{
if (fXmlSerializer == null)
{
fXmlSerializer = new XmlSerializer(typeof(T));
}
using (var ms = new MemoryStream())
{
//XmlSerializer不給Encoding,其XML宣告會是UTF-16 ;而且不能直接用Encoding.UTF8
var xmlWriter = new XmlTextWriter(ms, fUtf8);
XmlSerializerNamespaces xsnp = new XmlSerializerNamespaces();
xsnp.Add(string.Empty, string.Empty);
fXmlSerializer.Serialize(xmlWriter, item, xsnp);
string rst = Encoding.UTF8.GetString(ms.ToArray());
return rst;
}
}
/// <summary>
/// 获取Xml段;不带Xml头
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public static string ToXmlRemoveHead(T item)
{
var result = ToXml(item);
if (!string.IsNullOrEmpty(result))
{
var firtPos = result.IndexOf("?>");
if (firtPos > -1)
{
result = result.Remove(0, firtPos + 2);
}
}
return result;
}
public static T FromXml(string str)
{
if (fXmlSerializer == null)
{
fXmlSerializer = new XmlSerializer(typeof(T));
}
using (XmlReader reader = new XmlTextReader(new StringReader(str)))
{
return (T)fXmlSerializer.Deserialize(reader);
}
}
}
1、常规XML示例
<?xml version="1.0" encoding="utf-8" ?>
<root>
<title>标题</title>
<items>
<item>
<code>C001</code>
<name>张三</name>
</item>
<item>
<code>C002</code>
<name>李四</name>
</item>
</items>
<details>
<detail key="tel" value="123" />
<detail key="mobile" value="456" />
</details>
</root>
较为常用的XML中含有:
- XML元素:
<title>标题</title>
- XML属性:
key="tel"
- XML子节点:元素子节点
<item>
及属性子节点<detail>
一般一个接口有多个子节点也只用一种子节点类型,此为示例列出两种
1.1、实体类定义
VS提供了工具
左边为用VS粘贴生成的代码,我喜欢手动改为右边的定义。
1.2、实体类示例
[XmlRoot("root")]
public partial class SampleRoot
{
[XmlElement("title")]
public string Title { get; set; }
[XmlArray("items"), XmlArrayItem("item")]
public RootItem[] items { get; set; }
[XmlArray("details"), XmlArrayItem("detail")]
public RootDetail[] Details { get; set; }
}
[XmlRoot("item")]
public class RootItem
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("name")]
public string Name { get; set; }
}
[XmlRoot("detail")]
public class RootDetail
{
[XmlAttribute("key")]
public string Key { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
}
1.3、XML与实体类互转,即序列化与反序列化
用上面提供的工具类,非常方便:
//读出xml
var xml = File.ReadAllText("Sample.xml");
//反序列化,转成实体
var entity = XmlParser<SampleRoot>.FromXml(xml);
//序列化,转成字符串
var xml2 = XmlParser<SampleRoot>.ToXml(entity);
调试看出,XML转成对象之后再转成XML,得到当初一样的XML内容。
该工具类在反序列化读取XML时能自动读出CDATA节点内容,在序列化时对于需要转义的字符也会进行转义。产生的字符串是格式良好能通过验证的有效XML。
在大多数正常的系统对接中,通常上面的定义可以解决碰到的大多数对接需求;
如果是新接口定义,也建议有条件的话按这种子节点层次分明没有过多的格式要求来定义结构实现业务。子节点只用元素或只用属性,各就各位,所谓元素属性子节点,简单规范好理解。
当然现实中不可避免,会有一些其他的需求。
2、进一步的需求
2.1、节点有属性有子节点;举例code名字也相同
<?xml version="1.0" encoding="utf-8" ?>
<root>
<title>标题</title>
<items>
<item code="0">
<code>C001</code>
<name>张三</name>
</item>
<item code="1">
<code>C002</code>
<name>李四</name>
</item>
</items>
</root>
2.2.1、对应实体
[XmlRoot("root")]
public partial class SampleRoot
{
[XmlElement("title")]
public string Title { get; set; }
[XmlArray("items"), XmlArrayItem("item")]
public RootItem[] items { get; set; }
}
[XmlRoot("item")]
public class RootItem
{
//属性code
[XmlAttribute("code")]
public string Index { get; set; }
//元素code
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("name")]
public string Name { get; set; }
}
2.2、父节点有属性,子节点有属性且有元素值
<?xml version="1.0" encoding="utf-8" ?>
<root>
<title>标题</title>
<details name="明细">
<detail key="tel">张三</detail>
<detail key="mobile">李四</detail>
</details>
</root>
2.2.1、对应实体
[XmlRoot("root")]
public class SampleRoot
{
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("details")]
public Details Details { get; set; }
}
public class Details
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("detail")]
public Detail[] DetailList { get; set; }
}
public class Detail
{
[XmlAttribute("key")]
public string Key { get; set; }
[XmlText()]
public string Value { get; set; }//可以叫Value,或其他任何名字
}
3、特殊需求
3.1、序列化要输出CDATA节点
工具类读取XML为实体时自动支持CDATA节点,输出成XML要加上CDATA要这样定义
[XmlRoot("item")]
public partial class RootItem
{
[XmlAttribute("code")]
public string Index { get; set; }
//[XmlElement("code")]
[XmlIgnore]
public string Code { get; set; }
//[XmlElement("value")]
[XmlIgnore]
public int Value { get; set; }
}
public partial class RootItem
{
[XmlElement("code")]
public XmlNode _Code
{
get
{
var node = new XmlDocument().CreateNode(XmlNodeType.CDATA, "", "");
node.InnerText = Code;
return node;
}
set { Code = value.InnerText; }
}
[XmlElement("value")]
public XmlNode _Value
{
get
{
var node = new XmlDocument().CreateNode(XmlNodeType.Text, "", "");
node.InnerText = Value.ToString();
return node;
}
set { Value = Convert.ToInt32(value.InnerText); }
}
}
3.1.1、应用效果
VS格式化后对比
XML格式检查,格式化显示;最好用的工具依然是宇宙最强IDE-VS
3.2、不区分大小写
所谓不区分大小写,主要是指反序列化时,对XML元素/属性不区分大小写的转成指定实体类。
严格的节点不区分大小写,没有实现;网上有个方案说先将XML字符串做一次全部转小写(或大写)这样节点固定了就能读出来;如果业务数据也允许不分区大小写,是个办法。但是对业务数据做了假设,不太好。
根据碰到的情况,主要是节点首字母不区分大小写(有的产品用的是大驼峰:UserName,有的产品用的是小驼峰:userName),如下方法取巧。
示例XML,Code/Name和code/name都要支持
<?xml version="1.0" encoding="utf-8" ?>
<root>
<title>标题</title>
<items>
<item code="0">
<code>C001</code>
<name>张三</name>
</item>
<item code="1">
<Code>C002</Code>
<Name>李四</Name>
</item>
</items>
</root>
这种情况如下定义实体类解决;
大小驼峰各定义一次,使用的地方用任意一个即可(实体类定义修改,使用不修改)
[XmlRoot("item")]
public partial class RootItem
{
[XmlAttribute("code")]
public string Index { get; set; }
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("Code")]
public string _Code { get { return Code; } set { Code = value; } }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("Name")]
public string _Name { get { return Name; } set { Name = value; } }
}
使用的地方原先用Code/Name,继续用;_Code/_Name只用于XML解释。
同样如果再有产品大小驼峰都不用,用蛇式(user_name)或烤肉串式(user-name)命名法,类似再多定义下。规避不能完全支持节点不区分大小写的问题。
4、扩展不展
XML格式在文件存储中也很常用,Word文档可以用XML存储,PowerDesigner的PDM文件也是XML格式,解释这类复杂的XML可能要用到xslt,比如是否能支持完全不区分大小写的XML读取,这块用的很少没有实际接触,不展开/展不开。
5、平台杂谈
阿里的奇门接口主要支持XML格式;在与各大厂平台对接中对阿里系对接很有好感;他们先做的业务其他平台可以直接参考(最推荐是直接抄^-^
),技术提供的SDK也是真实有效,很多其他大厂要不没有SDK,要不SDK连编译都通不过。也许是我们接的太快了,或者是他们项目外包了。
奇门SDK上对XML的处理类,好像写的“复杂”了些,你看出来了吗?
感谢你看到这里,希望对你有帮助
XML序列化与反序列化接口对接实战,看这篇就够了的更多相关文章
- C#实现接口xml序列化与反序列化
C#实现接口xml序列化与反序列化 C#中接口无法被xml序列化,提示不支持.百度和bing也搜不到,只好自己动手写了 原理上肯定支持,.Net自己的xml序列化有一个IXmlSerializab ...
- XmlSerializer 对象的Xml序列化和反序列化
http://www.cnblogs.com/yukaizhao/archive/2011/07/22/xml-serialization.html 这篇随笔对应的.Net命名空间是System.Xm ...
- XmlSerializer 对象的Xml序列化和反序列化,XMLROOT别名设置
这篇随笔对应的.Net命名空间是System.Xml.Serialization:文中的示例代码需要引用这个命名空间. 为什么要做序列化和反序列化? .Net程序执行时,对象都驻留在内存中:内存中 ...
- Windows phone 之XML序列化与反序列化
为什么要做序列化和反序列化? 一个回答: 我们都知道对象是不能在网络中直接传输的,不过还有补救的办法.XML(Extensible Markup Language)可扩展标记语言,本身就被设计用来存储 ...
- C#操作Xml:XmlSerializer 对象的Xml序列化和反序列化
这篇随笔对应的.Net命名空间是System.Xml.Serialization:文中的示例代码需要引用这个命名空间. 为什么要做序列化和反序列化? .Net程序执行时,对象都驻留在内存中:内存中的对 ...
- .NET中XML序列化和反序列化常用类和用来控制XML序列化的属性总结(XmlSerializer,XmlTypeAttribute,XmlElementAttribute,XmlAttributeAttribute,XmlArrayAttribute...)
序列化和反序列化是指什么? 序列化(seriallization): 将对象转化为便于传输的数据格式, 常见的序列化格式:二进制格式,字节数组,json字符串,xml字符串.反序列化(deserial ...
- XML 序列化与反序列化
XML序列化与反序列化 1.将一个类转化为XML文件 /// <summary> /// 对象序列化成XML文件 /// </summary> /// <param na ...
- C#的XML序列化及反序列化
webservice在工作中用到的很多,基本都是以XML格式问通讯内容,其中最关键的就是XML串的序列化及反序列化. XML的运用中有两种信息传递,一种为XML的请求信息,另一种为返回信息,要运用XM ...
- .NET XML序列化与反序列化
闲着没事,写了两个通用的XML序列化与反序列化的方法. 贴出来当作笔记吧! /// <summary> /// XML序列化 /// </summary> /// <ty ...
随机推荐
- 学习MyBatis必知必会(2)~MyBatis基本介绍和MyBatis基本使用
一.MyBatis框架基本介绍: 1.认识 MyBatis: MyBatis 是支持普通 SQL 查询,存储过程和高级映射的持久层框架,严格上说应该是一个 SQL 映射框架. 其前身是 iBatis, ...
- django之百度Ueditor富文本编辑器后台集成
Python3 + Django2.0 百度Ueditor 富文本编辑器的集成 百度富文本编辑器官网地址:http://fex.baidu.com/ueditor/ 疑问:为什么要二次集成? 答案:因 ...
- new JSONObject 无异常卡顿【Maven+Idea 导包不更新的小坑】
问题描述 今天在使用JSONObject过程中出现了一个非常不可思议的现象,我Junit测试没有问题,但是就是打开服务器运行的时候,结果就是出不来,经过多次测试发现代码竟然卡在了new JSONObj ...
- Linux下Mysql端口修改及防火墙开端口
用户权限问题:https://blog.csdn.net/weixin_43670802/article/details/103019598 Linux下修改Mysql默认的3306端口 如下: 1. ...
- PHP中的单引号跟双引号的区别
不同点: 单引号只能解析转义字符"'"和"\",其他的原样输出.
- getter/setter方法
1.setter方法 作用:用来设置成员变量,可以在方法里面过滤掉一些不合理的值 命名规范: 必须是对象方法 返回值类型为void 方法名必须以set开头,而且后面跟上成员变量名去掉"_&q ...
- Java和Js编码(encodeUrl)解码(decodeUrl)对空格的差异问题
今天解决一个问题的时候遇到了一个编码解码问题,记录一下. 1. Js用的是encodeURIComponent()方法编码,后面的都以该编码方式处理出来的数据为准. 2. Java用的是URLDeco ...
- 类(静态)变量和类(静态)static方法以及main方法、代码块,final方法的使用,单例设计模式
类的加载:时间 1.创建对象实例(new 一个新对象时) 2.创建子类对象实例,父类也会被加载 3.使用类的静态成员时(静态属性,静态方法) 一.static 静态变量:类变量,静态属性(会被该类的所 ...
- 申请免费的ssl通配符证书
吐曹: 为了给我网站配置免费的htpps证书费死劲了, 折腾了一天, 找阿里阿里给我反馈的和我自己看的一样, 没什么用 我用Certbot生成证书以后怎么访问都是阿里的免费的hppts证书, 我都把阿 ...
- C语言中各种输入函数之间的区别
以下内容全部来自Bay(百度百科) scanf的返回值 scanf()函数返回成功赋值的数据项数,读到文件末尾出错时则返回EOF. 如: scanf("%d%d", &a, ...