gpt4 book ai didi

c# - state Property 中的标记 PropertyName 将导致无效的 JSON 对象。使用自定义 JsonConverter

转载 作者:行者123 更新时间:2023-11-30 15:52:27 27 4
gpt4 key购买 nike

我正在尝试使用 Json.NET 和自定义序列化程序对 .NET 数据集进行序列化/反序列化。我知道你们中的许多人会告诉我不要这样做(我在其他帖子上看到过)我有充分的理由并希望继续沿着这条路走下去。

我的序列化基于这样一个事实,即 .NET DataSet 可以将其模式和数据导出到 XML,然后重新导入它们;在此基础上,我试图创建一个转换器,允许我捕获该 XML,将其转换为 JSON,然后将其转换回来并重新加载。我的实现如下...

class DataSetConverter : JsonConverter<DataSet>
{
public override DataSet ReadJson(JsonReader reader, Type objectType, DataSet existingValue, bool hasExistingValue, JsonSerializer serializer)
{
DataSet dataSet = new DataSet();
JObject jObject = JObject.Load(reader);

String json = jObject.ToString();
XDocument document = JsonConvert.DeserializeXNode(json);
using (MemoryStream memoryStream = new MemoryStream())
using (StreamWriter streamWriter = new StreamWriter(memoryStream))
{
streamWriter.Write(document.ToString(SaveOptions.None));
streamWriter.Flush();

memoryStream.Position = 0;
dataSet.ReadXml(memoryStream);
}

return dataSet;
}

public override void WriteJson(JsonWriter writer, DataSet dataSet, JsonSerializer serializer)
{
using (MemoryStream memoryStream = new MemoryStream())
{
dataSet.WriteXml(memoryStream, XmlWriteMode.WriteSchema);
using (StreamReader reader = new StreamReader(memoryStream))
{
memoryStream.Seek(0, SeekOrigin.Begin);
XDocument document = XDocument.Parse(reader.ReadToEnd());
writer.WriteRaw(JsonConvert.SerializeXNode(document, Formatting.Indented, false));
}
}
}
}

按如下方式使用(纯粹序列化 DataSet 对象)它有效(我的新 DataSet 具有与原始数据集相同的模式和数据)...

DataSet originalInserts = new DataSet("Inserts");
DataTable originalStuff = originalInserts.Tables.Add("Stuff");

originalStuff.Columns.Add("C1", typeof(String));
originalStuff.Columns.Add("C2", typeof(Int64));
originalStuff.Columns.Add("C3", typeof(Guid));
originalStuff.Columns.Add("C4", typeof(float));

originalStuff.Rows.Add("One", 2, Guid.NewGuid(), 4.4);

String json = JsonConvert.SerializeObject(originalInserts, Formatting.Indented, new DataSetConverter());

DataSet newInsertsFromConvertedXml = (DataSet)JsonConvert.DeserializeObject(json, typeof(DataSet), new DataSetConverter());

但是,如果我随后尝试将同一个转换器与包含 DataSet 的对象(与上面的 DataSet 完全相同)...

public class TestClass
{
public DataSet Inserts { get; set; }

public String SomethingElse { get; set; }
}

TestClass testClass = new TestClass { Inserts = originalInserts, SomethingElse = "Me" };
json = JsonConvert.SerializeObject(testClass, Formatting.Indented, new DataSetConverter());

它失败了

Token PropertyName in state Property would result in an invalid JSON object. Path ''.

我还尝试使用 JsonConverter 属性装饰 TestClass 上的 DataSet 并从 Serialize 方法调用中删除转换器,但得到同样的结果...

public class TestClass
{
[JsonConverter(typeof(DataSetConverter))]
public DataSet Inserts { get; set; }

public String SomethingElse { get; set; }
}

TestClass testClass = new TestClass { Inserts = originalInserts, SomethingElse = "Me" };
json = JsonConvert.SerializeObject(testClass, Formatting.Indented);

我错过了什么?

最佳答案

你的基本问题是你应该调用 WriteRawValue()而不是 WriteRaw() :

writer.WriteRawValue(JsonConvert.SerializeXNode(document, Formatting.Indented, false)); 

documentation对于 WriteRawValue()状态:

Writes raw JSON where a value is expected and updates the writer's state.

documentation WriteRaw()状态:

Writes raw JSON without changing the writer's state.

无法推进作者的状态解释了为什么在尝试写入后续内容时抛出异常。

话虽如此,您正在创建一个很多 不必要的中间体 string , StreamJObject转换器内部的表示。一个更简单的方法是,在 WriteJson() 中到:

  1. 构造一个 XDocument并写下 DataSet使用 XContainer.CreateWriter() 直接访问它;

  2. 序列化 XDocument直接来电JsonWriter通过构建本地 XmlNodeConverter .

序列化将遵循相反的过程。因此你的 DataSetConverter看起来像:

class DataSetConverter : JsonConverter<DataSet>
{
public override DataSet ReadJson(JsonReader reader, Type objectType, DataSet existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.MoveToContent().TokenType == JsonToken.Null)
return null;
var converter = new XmlNodeConverter { OmitRootObject = false };
var document = (XDocument)converter.ReadJson(reader, typeof(XDocument), existingValue, serializer);
using (var xmlReader = document.CreateReader())
{
var dataSet = existingValue ?? new DataSet();
dataSet.ReadXml(xmlReader);
return dataSet;
}
}

public override void WriteJson(JsonWriter writer, DataSet dataSet, JsonSerializer serializer)
{
var document = new XDocument();
using (var xmlWriter = document.CreateWriter())
{
dataSet.WriteXml(xmlWriter, XmlWriteMode.WriteSchema);
}
var converter = new XmlNodeConverter { OmitRootObject = false };
converter.WriteJson(writer, document, serializer);
}
}

public static partial class JsonExtensions
{
public static JsonReader MoveToContent(this JsonReader reader)
{
// Start up the reader if not already reading, and skip comments
if (reader.TokenType == JsonToken.None)
reader.Read();
while (reader.TokenType == JsonToken.Comment && reader.Read())
{}
return reader;
}
}

注意事项:

  1. 您继承自 JsonConverter<DataSet> ,并在 ReadJson() 你构建了一个 DataSet 类型的对象直接地。但是,如 reference source 所示, JsonConverter<T>.CanConvert(Type objectType)适用于 T 类型的所有子类还有:

    public sealed override bool CanConvert(Type objectType)
    {
    return typeof(T).IsAssignableFrom(objectType);
    }

    因此您可能需要覆盖 CanConvert并使其仅在对象类型等于 typeof(DataSet) 时应用-- 但由于该方法已被密封,您不能。因此,可能证明有必要从非泛型基类继承 JsonConverter 相反。

关于c# - state Property 中的标记 PropertyName 将导致无效的 JSON 对象。使用自定义 JsonConverter<T> 时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54441810/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com