问题复现
一个对象需要转成json 后转成byte[]后经过网络传输,后再次反序列化为对象,但是最后反序列的时候会报错,打印json发现开头是一个问号
省流
使用这个进行反序列化
/// <summary>/// 反序列化方法/// </summary>/// <typeparam name="T"></typeparam>/// <param name="json"></param>/// <returns></returns>public static T Deserialize<T>(string json) where T : Message, new(){return JsonConvert.DeserializeObject<T>(json.TrimStart('\uFEFF'));}
问题代码
一个对象
using System.Threading;namespace SocketTools
{public class TextMessage : Message{private string message;public string Message { get => message; set => message = value; }public TextMessage(){}public TextMessage(string userName, string targetName, string sendTime, string message) : base(userName, targetName, sendTime, MessageType.Text){this.message = message;}}
父类
using Newtonsoft.Json;namespace SocketTools
{public enum MessageType{Connection = 0,Text,Image,Mixed,Recall,File,}public class Message{private string userName;private string targetName;private string sendTime;private MessageType messageType;public string UserName { get => userName; set => userName = value; }public string TargetName { get => targetName; set => targetName = value; }public string SendTime { get => sendTime; set => sendTime = value; }public MessageType MessageType { get => messageType; set => messageType = value; }public Message(){}public Message(string userName, string targetName, string sendTime, MessageType messageType){UserName = userName;TargetName = targetName;SendTime = sendTime;MessageType = messageType;}// 序列化方法public static string Serialize(Message message){return JsonConvert.SerializeObject(message);}// 反序列化方法public static T Deserialize<T>(string json) where T : Message, new(){ return JsonConvert.DeserializeObject<T>(json );}}}}
执行代码
using SocketTools;
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;namespace FunctionalTesting
{internal class Program{private static void Main(string[] args){string msg = "消息内容Q1_2_end";string userName = "AAAAAAAAAAAAAAAA";string targetUserName = "CCCCCCCCCCCCCCCC";TextMessage textMessage = new TextMessage(userName,targetUserName,DateTime.Now.ToString("yy-MM-dd-H:m:s"),msg);// 按照指定的格式创建消息字符串string message = Message.Serialize(textMessage);Console.WriteLine(message);// 将消息字符串转换为字节流byte[] data;using (MemoryStream ms = new MemoryStream()){using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8)){sw.Write(message);sw.Flush();ms.Position = 0;data = ms.ToArray();}}string data2 = Encoding.UTF8.GetString(data);Console.WriteLine(data2);data2 = Regex.Replace(data2, @"^\s+", "");Console.WriteLine(data2);var d = Message.Deserialize<TextMessage>(data2);Console.WriteLine(d.TargetName);Console.WriteLine(d.UserName);Console.WriteLine(d.MessageType);Console.ReadLine();}}
}
执行后的效果
执行之后会输出
?{"Message":"消息内容Q1_2_end","UserName":"AAAAAAAAAAAAAAAA","TargetName":"CCCCCCCCCCCCCCCC","SendTime":"23-12-04-1:14:43","MessageType":1}
错误日志:
Newtonsoft.Json.JsonReaderExceptionHResult=0x80131500Message=Unexpected character encountered while parsing value: . Path '', line 0, position 0.
但是打印原本的数据是没有开头的问号的,
解决方式
修改反序列化的代码,去除第一个前导字符即可,或者使用字符串的判断,如果第一个字符不是{
或者[
就去除第一个字符
// 反序列化方法public static T Deserialize<T>(string json) where T : Message, new(){ return JsonConvert.DeserializeObject<T>(json.TrimStart('\uFEFF'));}
去除字符串
public static T Deserialize<T>(string json) where T : Message, new(){ if (json[0] == '{' || json[0] == '['){}else{json = json.Remove(0, 1);}return JsonConvert.DeserializeObject<T>(json);}
问题原因
零宽度不中断空格(Unicode U+FEFF)字符,也称为 BOM(Byte Order Mark),在文本文件中用于表示字节顺序。它通常出现在使用 Unicode 编码的文本文件或数据流的开头。
在处理多字节编码时,需要知道每个字符的字节顺序。例如,在 UTF-16 编码中,一个字符由两个字节组成,而这两个字节的顺序可能会根据不同的系统和平台有所不同。为了明确表示字节顺序,引入了 BOM 字符。
BOM 字符 U+FEFF 可以作为文件的前导字符,允许解析器识别文件的字节顺序。当解析器遇到 U+FEFF 时,它可以确定字节顺序并正确地解码文件中的其余字符。
在某些情况下,尤其是在网络通信、数据库存储或其他形式的数据交换中,BOM 字符可能被无意间包含在字符串中,这可能导致解析错误。因此,有必要在处理这些字符串之前检查并移除任何不需要的 BOM 字符。