最近遇到对象在序列化与反序列化过程中出现异常。下面就来详细说说这个“对象转成json后转成byte[]后再转成string,反序列化时提示失败且第一个字符是问号”的问题,以及该如何解决。

一、问题复现

在实际开发场景中,我们需要将一个对象先转成json格式,再把json转成byte[] ,通过网络传输后,再将其反序列化为对象。然而,在反序列化这一步却出现了错误。当打印出要反序列化的json字符串时,发现其开头多了一个问号,这就是导致反序列化失败的“罪魁祸首”。

为了让大家更清楚,下面看看具体的代码示例。

(一)相关类定义

  1. TextMessage类
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; } } } 

这个类继承自Message类,用于表示文本消息,包含消息内容等属性。

  1. 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 ); } } } 

Message类是一个基础类,定义了消息的一些通用属性和序列化、反序列化方法,其中MessageType是一个枚举类型,用于表示消息的类型。

(二)执行代码

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(); } } } 

在这段代码中,我们创建了一个TextMessage对象,将其序列化后转换为字节流,再转换回字符串,最后尝试反序列化。

(三)执行效果与错误日志

执行上述代码后,输出的json字符串开头出现了问号:

?{"Message":"消息内容Q1_2_end","UserName":"AAAAAAAAAAAAAAAA","TargetName":"CCCCCCCCCCCCCCCC","SendTime":"23-12-04-1:14:43","MessageType":1} 

同时,反序列化时抛出错误,错误日志如下:

Newtonsoft.Json.JsonReaderException HResult=0x80131500 Message=Unexpected character encountered while parsing value: . Path '', line 0, position 0. 

但打印原本未经过转换的对象序列化后的字符串,是没有开头这个问号的。

二、解决方式

针对这个问题,有两种常见的解决办法。

(一)去除前导字符

修改反序列化代码,直接去除字符串开头的前导字符。可以使用TrimStart方法,代码如下:

// 反序列化方法 public static T Deserialize<T>(string json) where T : Message, new() { return JsonConvert.DeserializeObject<T>(json.TrimStart('uFEFF')); } 

这里的uFEFF就是导致问题的特殊字符,通过TrimStart方法将其去除后,反序列化就能正常进行。

(二)条件判断后去除

还可以通过判断字符串的第一个字符来决定是否去除。如果第一个字符不是{ 或者[ ,就去除第一个字符,代码如下:

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编码的文本文件或数据流中,BOM字符用来表示字节顺序。比如在UTF – 16编码里,一个字符由两个字节组成,不同系统和平台下这两个字节的顺序可能不同,BOM字符就能帮助解析器确定字节顺序,正确解码文件中的其他字符。

但在网络通信、数据库存储等数据交换场景中,BOM字符可能会被意外包含在字符串里,这就会导致解析错误。所以在处理这类字符串前,检查并移除不需要的BOM字符是很有必要的。

通过以上对问题的复现、解决方式的介绍以及原因剖析,希望大家在遇到类似问题时,能够快速定位并解决。