C#Object类型的索引,序列化和反序列化

前言

最近在编写一篇关于标准Mes接口框架的文章。其中有一个非常需要考究的内容时如果实现数据灵活和可使用性强。因为考虑数据灵活性,所以我一开始选取了Object类型作为数据类型,Object作为数据Value字段,String作为数据Key字段,形态与Dic的模式比较相似。但是在实际使用过程中,由于Object可以存储任意类型,但是显性类型指向Object,Type指向数据源类型。但是在进行Xml和Json序列化时候就出现了问题,Json和Xml无法序列化Object,这个非常合理,因为Object类型是没有指向的,序列化和反序列化时,无法知道你的Object类型指向哪一种类型。因为这个原因,所以我自己根据Json和XMl的数据格式编写了一套序列化和反序列化的规则。

1.方法说明

我预先设计了一个为MesProcessData的类,他里面包含一个string的索引字段,和Object的存储字段。那么我Object字段中存储的时一组新的MesProcessData的数组,在每个新的MesProcessData中又可以存储新的数据或者数组。这个方式参考来自于C#的索引机制。C# 中 . 运算符主要用于访问类或对象的成员、命名空间中的类型,对象的名字代表的是每一级的索引。那么MesName就是我所属变量的索引,object就是索引的值。在类型判断中,我们可以通过判断Object的Type是否为Array的形式去判断Object是否存储的是MesProcessData的类对象,如果是则继续递归查找,如果不是则判断当前的MesName是否是我所需要的索引。这些内容说的非常绕口,所以下面实际使用代码演示一下

2.使用方式

csharp 复制代码
//将数据为Header,Body,Return的数据存储在Top中
 List<MesProcessData> Message = new List<MesProcessData>();
 List<MesProcessData> Head = new List<MesProcessData>();
 Head.Add(new MesProcessData { MesName = "MESSAGENAME", MesValue = dynamic.AddressInterface });
 Head.Add(new MesProcessData { MesName = "TRANSACTIONID", MesValue = MesApp.Instance.Const.NowTime.ToString("yyyyMMddHHmmssffff") + number });
 Head.Add(new MesProcessData { MesName = "MESSAGEID", MesValue = number.ToString() });
 Head.Add(new MesProcessData { MesName = "REPLYSUBJECTNAME", MesValue = MesApp.Instance.MyMesConfig.MesAddress + ":" + MesApp.Instance.MyMesConfig.CustomerA.Port });
 Message.Add(new MesProcessData { MesName = "Header", MesValue = Head.ToArray() });

 Message.Add(new MesProcessData { MesName = "Body", MesValue = datas.ToArray() });

 List<MesProcessData> MessageReturn = new List<MesProcessData>();
 MessageReturn.Add(new MesProcessData { MesName = "ReturnCode", MesValue = "" });
 MessageReturn.Add(new MesProcessData { MesName = "ReturnMessage", MesValue = "" });
 Message.Add(new MesProcessData { MesName = "Return", MesValue = MessageReturn.ToArray() });


 MesProcessData Top = new MesProcessData();
 Top.MesName = "Message";
 Top.MesValue = Message.ToArray();
csharp 复制代码
//根据上面代码存入的数据,查找数据中索引为Header.MESSAGENAME的值
string MesInterface = ProcessData.FindValueByPath(new string[] { "Header", "MESSAGENAME" }, 0).ToString();
csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;

namespace Const
{
    public class MesProcessData
    {
        public string MesName { get; set; } = "";
        public object MesValue { get; set; }
        /// <summary>
        /// 根据指定路径找到对应的值
        /// </summary>
        /// <param name="path">数组路径,从下一级参数开始</param>
        /// <param name="index">当前使用的路径开始编号。仅在函数递归时使用</param>
        /// <returns></returns>
        public object FindValueByPath(string[] path, int index = 0)
        {
            if (index >= path.Length)
            {
                return this.MesValue;
            }
            if (this.MesValue is Array)
            {
                Array array = this.MesValue as Array;
                List<MesProcessData> datas = array.OfType<MesProcessData>().ToList();
                foreach (MesProcessData child in datas)
                {
                    if (child.MesName == path[index])
                    {
                        return child.FindValueByPath(path, index + 1);
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// 根据指定路径来修改参数值
        /// </summary>
        /// <param name="path">数组路径,从下一级参数开始</param>
        /// <param name="newValue">新的值</param>
        /// <param name="index">当前使用的路径开始编号。仅在函数递归时使用 </param>
        /// <returns></returns>
        public MesProcessData ModifyValueByPath(string[] path, object newValue, int index = 0)
        {
            if (index >= path.Length)
            {
                this.MesValue = newValue;
                return this;
            }

            if (this.MesValue is Array array)
            {
                List<MesProcessData> datas = array.OfType<MesProcessData>().ToList();
                foreach (MesProcessData child in datas)
                {
                    if (child.MesName == path[index])
                    {
                        // 递归修改下一级节点
                        child.ModifyValueByPath(path, newValue, index + 1);
                    }
                }
            }
            return this;
        }


        public string ObjectToString()
        {
            if (this.MesValue is string strArray)
            {
                return strArray;
            }
            else
            {
                return null;
            }
        }
        public List<MesProcessData> ObjectToData()
        {
            List<MesProcessData> datas = new List<MesProcessData>();
            if (this.MesValue is Array)
            {
                Array array = this.MesValue as Array;

                datas = array.OfType<MesProcessData>().ToList();
            }
            return datas;
        }
    }

}

3.序列化和反序列化

参照上面的内容我们可以分析,当判断为数组时,我们进行递归,直到递归到没有数组值为止,那么Name作为Key,MesValue作为Value。

csharp 复制代码
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;


namespace Model
{
    internal class MesJson
    {
        public static T DeserializeObject<T>(string json)
        {
            return JsonConvert.DeserializeObject<T>(json);
        }
        public static string SerializeObject<T>(T Data)
        {
            return JsonConvert.SerializeObject(Data);
        }

        /// <summary>
        /// 反序列化为 MesProcessData
        /// </summary>
        /// <param name="Json"></param>
        /// <returns></returns>
        public static MesProcessData DeserializeJson(string Json)
        {
            // 解析 JSON 字符串为 JObject
            JObject jsonObject = JObject.Parse(Json);
            MesProcessData data = new MesProcessData();
            // 开始遍历解析 JSON 对象
            data = TraverseJsonNodes(jsonObject);
            return data;
        }

        private static MesProcessData TraverseJsonNodes(JObject jsonObject)
        {
            MesProcessData data = new MesProcessData();
            data.MesName = jsonObject.Path;
            if (jsonObject.Count == 1)
            {
                data.MesValue = jsonObject.First.ToString();
            }
            List<MesProcessData> datas = new List<MesProcessData>();
            foreach (var item in jsonObject)
            {
                if (item.Value.Type == JTokenType.String)
                {
                    MesProcessData Jdata = new MesProcessData();

                    Jdata.MesName = item.Key.ToString();
                    Jdata.MesValue = item.Value.ToString();
                    datas.Add(Jdata);
                }
                else
                    datas.Add(TraverseJsonNodes((JObject)item.Value));
            }
            data.MesValue = datas.ToArray();
            return data;
        }
        private static readonly object Lock = new object();

        /// <summary>
        /// 将传入的 Object 对象序列化为 JSON 格式
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SerializeToJson(object obj)
        {
            try
            {
                lock (Lock)
                {
                    StringBuilder sb = new StringBuilder();
                    SerializeObject(obj, sb);
                    return sb.ToString();
                }
            }
            catch (Exception ex)
            {
                MesLog.Error("Mes Json序列化失败:" + ex.ToString());
                return "";
            }
        }

        private static void SerializeObject(object obj, StringBuilder sb)
        {
            if (obj == null)
            {
                sb.Append("null");
                return;
            }

            Type type = obj.GetType();

            if (type.IsPrimitive || type == typeof(string))
            {
                sb.Append(JsonValue(obj));
                return;
            }

            if (type.IsArray)
            {
                sb.Append("[");
                Array array = (Array)obj;
                for (int i = 0; i < array.Length; i++)
                {
                    if (i > 0)
                    {
                        sb.Append(",");
                    }
                    SerializeObject(array.GetValue(i), sb);
                }
                sb.Append("]");
                return;
            }

            sb.Append("{");
            sb.AppendLine();
            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            bool first = true;
            foreach (FieldInfo field in fields)
            {
                object fieldValue = field.GetValue(obj);
                if (fieldValue == null)
                {
                    continue;
                }
                if (!first)
                {
                    sb.Append(",");
                    sb.AppendLine();
                }
                first = false;
                sb.Append($"\"{field.Name}\": ");
                SerializeObject(fieldValue, sb);
            }
            sb.AppendLine();
            sb.Append("}");
        }

        private static string JsonValue(object obj)
        {
            if (obj == null)
            {
                return "null";
            }
            if (obj is string str)
            {
                return $"\"{EscapeString(str)}\"";
            }
            if (obj is bool boolValue)
            {
                return boolValue.ToString().ToLower();
            }
            if (obj is char)
            {
                return $"\"{obj}\"";
            }
            return obj.ToString();
        }

        private static string EscapeString(string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if (c == '\\' || c == '"')
                {
                    sb.Append('\\');
                }
                sb.Append(c);
            }
            return sb.ToString();
        }
    }
}
csharp 复制代码
using JOJO.Mes.Const;
using JOJO.Mes.Log;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace JOJO.Mes.Model
{
    internal class MesXml
    {

        /// <summary>
        /// 不带反馈的发送信息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="obj"></param>
        public static async void SendObjectAsXml(Socket socket, string xmlString)
        {
            try
            {
                byte[] xmlBytes = Encoding.UTF8.GetBytes(xmlString);
                await Task.Run(() =>
                {
                    // 通过Socket发送数据
                    socket.Send(xmlBytes, 0, xmlBytes.Length, SocketFlags.None);
                });
            }
            catch (Exception ex)
            {
                MesLog.Error("发送不带反馈的Socket数据失败:" + ex.ToString());
            }
        }
        public static T DeserializeObject<T>(string Xml)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            StringReader stringReader = new StringReader(Xml);
            T deserializedData = (T)serializer.Deserialize(stringReader);
            stringReader.Dispose();
            return deserializedData;
        }
        /// <summary>
        /// 反序列化为MesProcessData
        /// </summary>
        /// <param name="Xml"></param>
        /// <returns></returns>
        public static MesProcessData DeserializeXml(string Xml)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(Xml);

            MesProcessData data = new MesProcessData();
            data = TraverseNodes(xmlDoc.DocumentElement);
            return data;
        }
        static MesProcessData TraverseNodes(XmlNode node)
        {
            MesProcessData data1 = new MesProcessData();
            data1.MesName = node.Name;
            if (node.HasChildNodes)
            {
                if (node.FirstChild.NodeType == XmlNodeType.Text)
                {
                    data1.MesValue = node.InnerText;
                    return data1;
                }
                List<object> datas = new List<object>();
                foreach (XmlNode childNode in node.ChildNodes)
                {
                    datas.Add(TraverseNodes(childNode));

                }
                data1.MesValue = datas.ToArray();
            }
            else
            {
                data1.MesValue = node.InnerText;
            }
            return data1;
        }

        private static readonly object Lock = new object();
        /// <summary>
        /// 将传入的Object对象序列化为XML格式
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string SerializeToXml(object obj)
        {
            try
            {
                lock (Lock)
                {
                    StringWriter stringWriter = new StringWriter();

                    using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter))
                    {
                        //添加输入对象的头,
                        xmlWriter.WriteStartDocument();
                        FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                        foreach (FieldInfo field in fields)
                        {
                            object fieldValue = field.GetValue(obj);
                            string fieldName = field.Name;

                            if (fieldValue != null)
                            {
                                Type valueType = fieldValue.GetType();
                                if (valueType.IsPrimitive || valueType == typeof(string) || !valueType.IsArray)
                                {
                                    if (field.Name.Contains("MesValue"))
                                    {
                                        xmlWriter.WriteValue(fieldValue);
                                    }
                                    else if (field.Name.Contains("MesName"))
                                    {
                                        xmlWriter.WriteStartElement(fieldValue.ToString());
                                        continue;
                                    }
                                    else
                                    {
                                        xmlWriter.WriteStartElement(field.FieldType.Name.ToString());
                                        xmlWriter.WriteValue(fieldValue);
                                        xmlWriter.WriteEndElement();
                                        continue;
                                    }
                                    xmlWriter.WriteEndElement();

                                }
                                else if (valueType.IsArray)
                                {

                                    Array array = (Array)fieldValue;
                                    foreach (object item in array)
                                    {
                                        if (item != null)
                                        {
                                            string subXml = SerializeToXml(item);
                                            xmlWriter.WriteRaw(subXml);
                                        }
                                    }

                                }
                                else
                                {
                                    string subXml = SerializeToXml(fieldValue);
                                    xmlWriter.WriteRaw(subXml);
                                    string xml = xmlWriter.ToString();
                                }
                            }
                        }
                        //xmlWriter.WriteEndElement();
                        xmlWriter.WriteEndDocument();
                    }
                    return stringWriter.ToString();
                }
            }
            catch (Exception ex)
            {
                MesLog.Error("序列化XML失败:" + ex.ToString());
                return "";
            }
        }
        private static XElement RecursiveProcess(XElement element)
        {
            List<XElement> newChildren = new List<XElement>();
            foreach (XElement child in element.Elements())
            {
                XElement name = child.Element("MesName");
                XElement value = child.Element("MesValue");
                if (name != null && value != null)
                {
                    XElement newElement;
                    if (value.HasElements)
                    {
                        // 如果Value有子元素,递归处理
                        XElement processedValue = RecursiveProcess(value);
                        newElement = new XElement(name.Value, processedValue.Nodes());
                    }
                    else
                    {
                        newElement = new XElement(name.Value, value.Value);
                    }
                    newChildren.Add(newElement);
                }
                else
                {
                    if (child.Nodes().Count() > 1)
                    {
                        // 如果没有Name和Value,继续递归处理子元素
                        XElement recursivelyProcessedChild = RecursiveProcess(child);
                        newChildren.Add(recursivelyProcessedChild);
                    }
                    else
                    {
                        newChildren.Add(child);
                    }

                }
            }
            element.RemoveAll();
            element.Add(newChildren);
            return element;
        }
    }

}

4.结论

这种方式限定了数据的规则,但是核心原理是一致的。在序列化中,对于可读序列化而言,只有值和数组值的区分。所以我们在控制Object类型序列化时也是参考这个方式,判断是否为数组即可,如果数据格式跟我的不一样的话,那么可以使用反射的形式。例如在一个类中有部分字段为Object类型,那么我们使用反射的形式,将json映射Object中,同样我们也需要判断Json中的Object是否为数组,不是可以直接赋值,是的话则需要递归转换再赋值。将字段中的Object转换为Json也是同理,判断是否为数组,不是则直接转换,是则递归转换。

相关推荐
“抚琴”的人14 小时前
【机械视觉】C#+VisionPro联合编程———【六、visionPro连接工业相机设备】
c#·工业相机·visionpro·机械视觉
FAREWELL0007516 小时前
C#核心学习(七)面向对象--封装(6)C#中的拓展方法与运算符重载: 让代码更“聪明”的魔法
学习·c#·面向对象·运算符重载·oop·拓展方法
tadus_zeng16 小时前
Windows C++ 排查死锁
c++·windows
EverestVIP16 小时前
VS中动态库(外部库)导出与使用
开发语言·c++·windows
CodeCraft Studio16 小时前
Excel处理控件Spire.XLS系列教程:C# 合并、或取消合并 Excel 单元格
前端·c#·excel
勘察加熊人18 小时前
forms实现连连看
c#
hvinsion18 小时前
PPT助手:一款集计时、远程控制与多屏切换于一身的PPT辅助工具
c#·powerpoint·ppt·ppt助手·ppt翻页
weixin_3077791319 小时前
使用C#实现从Hive的CREATE TABLE语句中提取分区字段名和数据类型
开发语言·数据仓库·hive·c#
抛物线.19 小时前
inhibitor_tool
windows
时光追逐者20 小时前
在 Blazor 中使用 Chart.js 快速创建数据可视化图表
开发语言·javascript·信息可视化·c#·.net·blazor