Token PropertyName in state Property would result in an invalid JSON object. when using custom JsonConverter

后端 未结 1 1382
执笔经年
执笔经年 2021-01-19 16:28

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

1条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-19 16:34

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

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

    The documentation for WriteRawValue() states:

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

    Whereas the documentation WriteRaw() states:

    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.

    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. Construct an XDocument and write the DataSet directly to it using XContainer.CreateWriter();

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

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

    class DataSetConverter : JsonConverter
    {
        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;
        }
    }
    

    Notes:

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

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

      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.

    0 讨论(0)
提交回复
热议问题