在我们提供接口服务给第三方调用时,一般会采用Request/Response模式,即请求与响应都采用统一的外部封装,真正的业务数据则由Request/Resonse的某个参数比如Data之类的类进行承担,以Request为例,该请求类假设定义成如下内容:
///
/// 数据请求类
///
///
[XmlRoot("Request")]
public class Request
{
///
/// 请求外部唯一性流水号,用于Resposne对应
///
public string RequestId { get; set; }
///
/// 请求日期 yyyy-MM-dd HH:mm:ss格式
///
public string RequestDate { get; set; }
///
/// 请求业务数据
///
public T Data { get; set; }
}这里初始化该类的一个具体定义
Request request = new Request
{
Data = 1,
RequestDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
RequestId = Guid.NewGuid().ToString()
}其Xml序列化结果默认如下
6a9a99fc-7731-41d9-87b1-2cc637b0afdc
2018-03-07 14:38:08
1
这里业务数据部分不管泛型类为什么,该业务的
Xml节点名称均为Data,这是很正常的一种数据契约定义方式,但实际我们接入第三方接口时,很可能会碰到另外一种情况,甚至可以说是反人类的定义方式,同一个第三方接口服务,
接口A和接口B的业务数据Xml节点名称不一样!!!具体可能如下
6a9a99fc-7731-41d9-87b1-2cc637b0afdc
2018-03-07 14:38:08
1
这种情况下,前面的Request肯定是无法支持的,所以需要对Request进行调整
///
/// 数据请求类
///
///
[XmlRoot("Request")]
public abstract class Request
{
///
/// 请求外部唯一性流水号,用于Resposne对应
///
public string RequestId { get; set; }
///
/// 请求日期 yyyy-MM-dd HH:mm:ss格式
///
public string RequestDate { get; set; }
///
/// 请求业务数据
///
[XmlIgnore]
public abstract T Data { get; set; }
}注意除了
Data属性被定义成了abstract外,该节点还设置了XmlIgnore特性,如果不设置该特性,那么子类自定义XmlElement后再进行Xml序列化会产生异常,下面是业务A的定义
[XmlRoot("Request")]
public class RequestForA : Request
{
[XmlElement("BusinessA")]
public override int Data { get; set; }
}注意该类也
必须要定义XmlRoot特性,否则序列化出来的Xml最外层节点名称将为RequestForA,同理对于业务B也需要如此操作设置(再说一次这么提供接口服务的第三方真是反人类!!!)
最后实际进行业务定义的地方进行调整
var request = new RequestForA
{
Data = 1,
RequestDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
RequestId = Guid.NewGuid().ToString()
};这样就可以对不同的业务,序列化不同的Xml节点名称(当然反序列化也是没有任何问题的),顺带补充下Xml序列化辅助类
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
///
/// xml序列化辅助类
///
public static class XmlHelper
{
///
/// xml序列化
///
/// 泛型
/// 待序列化对象
/// 字符编码,不指定则utf-8
/// 是否显示xml声明
/// 是否移除默认的xmlns:xsi命名空间(注:如果待序列化对象指定了NameSpace还是会序列化出对应的命名空间)
///
public static string Serializer(this T obj, Encoding encoding = null, bool showDeclaration = true, bool removeDefaultNameSpace = false)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
if (encoding == null)
{
encoding = Encoding.UTF8;
}
using (MemoryStream stream = new MemoryStream())
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.Indent = true;
xws.OmitXmlDeclaration = !showDeclaration;
xws.Encoding = encoding;
using (XmlWriter xtw = XmlWriter.Create(stream, xws))
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
if (removeDefaultNameSpace)
{
ns.Add("", "");
}
serializer.Serialize(xtw, obj, ns);
stream.Position = 0;
string xml = encoding.GetString(stream.GetBuffer());
//这种方法生成的xml字符串在不同Encoding下不知道为啥可能会有不可见字符
if (xml[0] != '
{
var sIdx = xml.IndexOf('
var eIdx = xml.LastIndexOf('>');
xml = xml.Substring(sIdx, eIdx - sIdx + 1);
//return Regex.Replace(xml, @"^[\s\S]*?(?=]*?$", string.Empty);//正则存在性能问题
}
return xml;
}
}
}
///
/// xml反序列化
///
/// 泛型
/// xml内容
///
public static T Deserialize(this string xml)
{
using (StringReader sr = new StringReader(xml))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
try
{
return (T)serializer.Deserialize(sr);
}
catch(Exception e)
{
return default(T);
}
}
}
}好吧,上面写了那么多,实际这么做每种业务服务都要定义一个class定义,一旦业务多了,对于强迫症或者代码洁癖者可能是一种折磨,毕竟每种业务除了自身的业务类定义外,还需要定义外部包含类,那是否还有其他可行的方法呢?
答案是肯定的,但这种答案是彻底定制的!!!定制的思路是这样的,既然你的业务节点名称不同,那在序列化或反序列化时,针对这特定的业务名称进行特殊处理不就行了?下面是以LinqToXml进行Xml解析的示例代码:
///
/// 完全定制的xml序列化类,只可做参考,不能直接拿来用
///
public class XmlHelperCustomized
{
///
/// 序列化
///
///
///
/// 要替换的业务节点名称
///
public static string Serializer(Request request, string nodeName)
{
var xml = XmlHelper.Serializer(request);
var root = XElement.Parse(xml);
var ele = root.Element("Data");
if (ele != null)
{
ele.Name = nodeName;
}
return root.ToString();
}
///
/// 反序列化
///
///
///
/// 业务节点名称
/// 业务数据是否需要xml反序列化,true表示是
///
public static Request Deserialize(string xml, string nodeName, bool dataIsXml)
{
var request = XmlHelper.Deserialize>(xml);
var root = XElement.Parse(xml);
var ele = root.Element(nodeName);
if (ele != null)
{
T obj;
if (dataIsXml)
{
obj = XmlHelper.Deserialize(ele.ToString());
}
else
{
obj = (T)Convert.ChangeType(ele.Value, typeof(T));
}
request.Data = obj;
}
return request;
}
}这里为该类业务定制了一个特定的Xml序列化类(注意该部分代码依赖上面的XmlHelper),相应的使用例子代码如下:
var request = new Request//注意这里的Request是非abstract的那个
{
Data = 1,
RequestDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
RequestId = Guid.NewGuid().ToString()
};
var xml = XmlHelperCustomized.Serializer(request, "BusinessA");
var requestA = XmlHelperCustomized.Deserialize(xml, "BusinessA", false);