章
目
录
最近遇到对象在序列化与反序列化过程中出现异常。下面就来详细说说这个“对象转成json后转成byte[]后再转成string,反序列化时提示失败且第一个字符是问号”的问题,以及该如何解决。
一、问题复现
在实际开发场景中,我们需要将一个对象先转成json格式,再把json转成byte[] ,通过网络传输后,再将其反序列化为对象。然而,在反序列化这一步却出现了错误。当打印出要反序列化的json字符串时,发现其开头多了一个问号,这就是导致反序列化失败的“罪魁祸首”。
为了让大家更清楚,下面看看具体的代码示例。
(一)相关类定义
- 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
类,用于表示文本消息,包含消息内容等属性。
- 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字符是很有必要的。
通过以上对问题的复现、解决方式的介绍以及原因剖析,希望大家在遇到类似问题时,能够快速定位并解决。