状态为Property的令牌PropertyName将导致无效的JSON对象.使用自定义JsonConverter< T>时 [英] Token PropertyName in state Property would result in an invalid JSON object. when using custom JsonConverter<T>

查看:111
本文介绍了状态为Property的令牌PropertyName将导致无效的JSON对象.使用自定义JsonConverter< T>时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to serialise/deserialise a .NET DataSet using Json.NET and a custom serialiser. I know many of you will tell me not to (I have seen this on other posts) I have a valid reason and wish to continue down this route.

我的序列化基于以下事实:.NET数据集可以将其架构和数据导出到XML,然后重新导入.在此基础上,我尝试创建一个转换器,该转换器将允许我捕获该XML,将其转换为JSON,然后将其转换回并重新加载.我的实现如下...

My serialisation is based upon the fact that the .NET DataSet can export it's schema and data to XML and then re-import the same; on that basis, I am trying to create a converter that will allow me to capture that XML, convert it to JSON then convert it back and reload it. My implementation is as follows...

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具有相同的架构和数据)...

Used as follows (purely serialising a DataSet object) it works (my new DataSet has the same schema and data as the original)...

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)一起使用...

However if I then try and use the same converter with an object that contains a DataSet (the very same DataSet as above)...

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

失败并显示

状态为Property的令牌PropertyName将导致无效的JSON 目的.路径.

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

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

I have also tried decorating the DataSet on the TestClass with a JsonConverter attribute and removing the converter from the Serialize method call but get the same result...

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():

Your basic problem is that you should be calling WriteRawValue() instead of WriteRaw():

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

WriteRawValue()状态的文档:

在期望值的地方写入原始JSON并更新编写者的状态.

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

文档 WriteRaw()指出:

在不更改编写者状态的情况下写入原始JSON.

Writes raw JSON without changing the writer's state.

无法提升作者的状态,这解释了为什么尝试写入后续内容时会引发异常.

The failure to advance the writer's state explains why an exception is thrown when an attempt is made to write subsequent content.

也就是说,您正在创建转换器内部不必要的中间stringStreamJObject表示形式的 lot .在WriteJson()中,更简单的方法是:

That being said, you're creating a lot of unnecessary intermediate string, Stream and JObject representations inside your converter. A simpler approach would be, in WriteJson() to:

  1. 构造一个XDocument并使用DataSet写入到其中. xcontainer.createwriter?view = netframework-4.7.2"rel =" nofollow noreferrer> XContainer.CreateWriter() ;

  1. Construct an XDocument and write the DataSet directly to it using XContainer.CreateWriter();

通过构造本地

Serialize the XDocument directly to the incoming JsonWriter by constructing a local XmlNodeConverter.

串行化将遵循相反的过程.因此,您的DataSetConverter看起来像是:

Serialization would follow the reverse process. Thus your DataSetConverter would look like:

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的对象直接地.但是,如参考源所示JsonConverter<T>.CanConvert(Type objectType)也适用于类型为T的所有子类:

  1. You are inheriting from JsonConverter<DataSet>, and in ReadJson() you construct an object of type DataSet directly. However, as shown in the reference source, JsonConverter<T>.CanConvert(Type objectType) applies to all subclasses of the type T also:

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

因此,您可能需要覆盖CanConvert并使其仅在对象类型等于typeof(DataSet)时才适用-但由于该方法已被密封,因此您不能.因此,可能有必要从非通用基类 JsonConverter继承代替.

Thus you may need to override CanConvert and make it apply only when the object type is equal to typeof(DataSet) -- but since the method has been sealed, you cannot. Therefore, it may prove necessary to inherit from the non-generic base class JsonConverter instead.

这篇关于状态为Property的令牌PropertyName将导致无效的JSON对象.使用自定义JsonConverter&lt; T&gt;时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆