Xml序列化当泛型不同时序列化(反序列化)为不同的Xml节点名称

1 篇文章 0 订阅
在我们提供接口服务给第三方调用时,一般会采用Request/Response模式,即请求与响应都采用统一的外部封装,真正的业务数据则由Request/Resonse的某个参数比如Data之类的类进行承担,以Request为例,该请求类假设定义成如下内容:
    /// <summary>
    /// 数据请求类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [XmlRoot("Request")]
    public class Request<T>
    {
        /// <summary>
        /// 请求外部唯一性流水号,用于Resposne对应
        /// </summary>
        public string RequestId { get; set; }
        /// <summary>
        /// 请求日期 yyyy-MM-dd HH:mm:ss格式
        /// </summary>
        public string RequestDate { get; set; }
        /// <summary>
        /// 请求业务数据
        /// </summary>
        public T Data { get; set; }
    }
这里初始化该类的一个具体定义

            Request<int> request = new Request<int>
            {
                Data = 1,
                RequestDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                RequestId = Guid.NewGuid().ToString()
            }
其Xml序列化结果默认如下
<?xml version="1.0" encoding="utf-8"?>
<Request xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RequestId>6a9a99fc-7731-41d9-87b1-2cc637b0afdc</RequestId>
  <RequestDate>2018-03-07 14:38:08</RequestDate>
  <Data>1</Data>
</Request>
这里业务数据部分不管泛型类为什么,该业务的 Xml节点名称均为Data,这是很正常的一种数据契约定义方式,但实际我们接入第三方接口时,很可能会碰到另外一种情况,甚至可以说是反人类的定义方式,同一个第三方接口服务, 接口A和接口B的业务数据Xml节点名称不一样!!!具体可能如下
<?xml version="1.0" encoding="utf-8"?>
<Request xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RequestId>6a9a99fc-7731-41d9-87b1-2cc637b0afdc</RequestId>
  <RequestDate>2018-03-07 14:38:08</RequestDate>
  <BusinessA>1</BusinessA><!--A服务的业务请求-->
  <!--<BusinessB>1</BusinessB>--><!--B服务的业务请求-->
</Request>
这种情况下,前面的Request<T>肯定是无法支持的,所以需要对Request<T>进行调整
    /// <summary>
    /// 数据请求类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [XmlRoot("Request")]
    public abstract class Request<T>
    {
        /// <summary>
        /// 请求外部唯一性流水号,用于Resposne对应
        /// </summary>
        public string RequestId { get; set; }
        /// <summary>
        /// 请求日期 yyyy-MM-dd HH:mm:ss格式
        /// </summary>
        public string RequestDate { get; set; }
        /// <summary>
        /// 请求业务数据
        /// </summary>
        [XmlIgnore]
        public abstract T Data { get; set; }
    }
注意除了 Data属性被定义成了abstract外,该节点还设置了XmlIgnore特性,如果不设置该特性,那么子类自定义XmlElement后再进行Xml序列化会产生异常,下面是业务A的定义
    [XmlRoot("Request")]
    public class RequestForA : Request<int>
    {
        [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;
    /// <summary>
    /// xml序列化辅助类
    /// </summary>
    public static class XmlHelper
    {
        /// <summary>
        /// xml序列化
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="obj">待序列化对象</param>
        /// <param name="encoding">字符编码,不指定则utf-8</param>
        /// <param name="showDeclaration">是否显示xml声明</param>
        /// <param name="removeDefaultNameSpace">是否移除默认的xmlns:xsi命名空间(注:如果待序列化对象指定了NameSpace还是会序列化出对应的命名空间)</param>
        /// <returns></returns>
        public static string Serializer<T>(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;
                }
            }
        }
        /// <summary>
        /// xml反序列化
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="xml">xml内容</param>
        /// <returns></returns>
        public static T Deserialize<T>(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解析的示例代码:

    /// <summary>
    /// 完全定制的xml序列化类,只可做参考,不能直接拿来用
    /// </summary>
    public class XmlHelperCustomized
    {
        /// <summary>
        /// 序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <param name="nodeName">要替换的业务节点名称</param>
        /// <returns></returns>
        public static string Serializer<T>(Request<T> 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();
        }
        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xml"></param>
        /// <param name="nodeName">业务节点名称</param>
        /// <param name="dataIsXml">业务数据是否需要xml反序列化,true表示是</param>
        /// <returns></returns>
        public static Request<T> Deserialize<T>(string xml, string nodeName, bool dataIsXml)
        {
            var request = XmlHelper.Deserialize<Request<T>>(xml);
            var root = XElement.Parse(xml);
            var ele = root.Element(nodeName);
            if (ele != null)
            {
                T obj;
                if (dataIsXml)
                {
                    obj = XmlHelper.Deserialize<T>(ele.ToString());
                }
                else
                {
                    obj = (T)Convert.ChangeType(ele.Value, typeof(T));
                }
                request.Data = obj;
            }
            return request;
        }
    }
这里为该类业务定制了一个特定的Xml序列化类(注意该部分代码依赖上面的XmlHelper),相应的使用例子代码如下:
            var request = new Request<int>//注意这里的Request<T>是非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<int>(xml, "BusinessA", false);


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中使用fastjson进行泛型类的反序列化,你可以按照以下步骤进行操作: 首先,确保你已经引入了fastjson的依赖,可以通过Maven等构建工具添加以下依赖项: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.79</version> </dependency> ``` 接下来,假设你有一个泛型类`Result<T>`,如下所示: ```java import com.alibaba.fastjson.JSON; public class Result<T> { private int code; private String message; private T data; public Result(int code, String message, T data) { this.code = code; this.message = message; this.data = data; } public int getCode() { return code; } public String getMessage() { return message; } public T getData() { return data; } public static <T> Result<T> fromJson(String json, Class<T> clazz) { return JSON.parseObject(json, new TypeReference<Result<T>>(clazz) {}.getType()); } } ``` 在上述代码中,`fromJSON`方法使用fastjson的`parseObject`方法将JSON字符串反序列化为`Result<T>`对象。 然后,你可以使用以下代码将JSON字符串反序列化为具体的泛型类对象: ```java import com.alibaba.fastjson.JSON; public class Main { public static void main(String[] args) { String json = "{\"code\":200,\"message\":\"Success\",\"data\":{\"name\":\"John\",\"age\":25}}"; Result<User> result = Result.fromJson(json, User.class); System.out.println("Code: " + result.getCode()); System.out.println("Message: " + result.getMessage()); User user = result.getData(); System.out.println("Name: " + user.getName()); System.out.println("Age: " + user.getAge()); } } ``` 在上述代码中,我们将一个包含`User`对象的JSON字符串反序列化为`Result<User>`对象,并输出其中的字段值。 请注意,为了正确反序列化泛型类,我们需要通过传递`Class<T>`参数给`fromJSON`方法来指定具体的泛型类型。 以上就是使用fastjson进行泛型反序列化的基本示例。当然,根据实际需求和数据结构的复杂程度,你可能需要进行更多的定制和处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值