前言
最近在编写一篇关于标准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.使用方式
//将数据为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();
//根据上面代码存入的数据,查找数据中索引为Header.MESSAGENAME的值
string MesInterface = ProcessData.FindValueByPath(new string[] { "Header", "MESSAGENAME" }, 0).ToString();
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。
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();
}
}
}
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也是同理,判断是否为数组,不是则直接转换,是则递归转换。