将Json映射到自定义类

时间:2017-01-05 07:56:45

标签: c# .net json json.net

我跟随json:

{"EventMessageUId":"ef51b5a3-32b2-e611-baf9-fc3fdb446bd2","Message":
"{\"StoryId\":20500,\"StoryDesc\":\"Test Story
data\"}","ProjectId":"1"}

以下是我正在尝试映射它的类:

public class Requirments
    {

        public int FileID { get; set; }
        public string EventMessageUId { get; set; }
        public string ProjectId { get; set; }
        public List<Message> Message { get; set; }
        //public object[] Message { get; set; }
        public string error { get; set; }
    }

将消息标记作为字符串

  "Message": "{\"StoryId\":20500,\"StoryDesc\":\"Test Story data\"}"

我想将其映射到List<Message>

 public class Message
    {
        public string StoryID { get; set; }
        public string StoryDesc { get; set; }
    }

如果不改变json,我该怎么做?

在当前情况下,当我使用List<Message>

进行尝试时,它会给我一个错误

5 个答案:

答案 0 :(得分:3)

这可能会为你做到这一点

string jsonstr = File.ReadAllText(YourJSONFile);
jsonstr = jsonstr.Replace("\"{", "{");
jsonstr = jsonstr.Replace("}\"", "}");
jsonstr = jsonstr.Replace("\\", "");
var ser = JsonConvert.DeserializeObject<MyMessages>(jsonstr);

这些类看起来像

public class Message
{
    [JsonProperty("StoryId")]
    public int StoryId { get; set; }
    [JsonProperty("StoryDesc")]
    public string StoryDesc { get; set; }
}

public class MyMessages
{
    [JsonProperty("Message")]
    public Message Message { get; set; }
}

JSON的问题是

"Message": "{\"StoryId\":20500,\"StoryDesc\":\"Test Story data\"}"
           ^                                                   ^  

是这些",它使它成为字符串而不是JSON的两个不同属性。我们已将"{}"

删除
jsonstr = jsonstr.Replace("\"{", "{");
jsonstr = jsonstr.Replace("}\"", "}");

现在剩下的JSON字符串将是

"Message": {\"StoryId\":20500,\"StoryDesc\":\"Test Story data\"}
            ^        ^        ^          ^  ^                ^

我们在JSON字符串中有\反斜杠,这会在解除JSON字符串的同时再次产生问题。所以

jsonstr = jsonstr.Replace("\\", "");

答案 1 :(得分:2)

将JSON反序列化到Requirments类时有两个不同的问题:

  1. Message属性包含嵌套的双序列化数据。即发送系统将原始Message对象序列化为JSON字符串,然后将其包含在随后自行序列化的外部容器对象中,从而导致内部JSON数据被转义。

  2. 嵌套的JSON表示单个对象 - 由大括号括起的名称/值对的集合。但是您希望反序列化为List<Message>,并且所有JSON序列化程序都会将List<T>映射到JSON数组而不是JSON对象。

  3. 使用结合custom JsonConverter Message属性可以克服这两个问题。但是,转换需要两个独立的步骤:

    1. 您需要将嵌套的双序列化JSON解包为字符串。

    2. 然后映射JSON对象,从而使用How to handle both a single item and an array for the same property using JSON.net中与SingleOrArrayConverter<T>类似的转换器展开。

    3. 以下转换器集执行此链式转换:

      public class SingleOrArrayConverter<TCollection, TItem> : SingleOrArrayConverter where TCollection : ICollection<TItem>
      {
          public override bool CanConvert(Type objectType)
          {
              if (!base.CanConvert(objectType))
                  return false;
              return typeof(TCollection).IsAssignableFrom(objectType);
          }
      }
      
      public class SingleOrArrayConverter : JsonConverter
      {
          public override bool CanConvert(Type objectType)
          {
              if (objectType.IsArray || objectType == typeof(string) || objectType.IsPrimitive)
                  return false;
              Type elementType = null;
              foreach (var type in objectType.GetCollectItemTypes())
              {
                  if (elementType == null)
                      elementType = type;
                  else
                      return false;
              }
              return elementType != null;
          }
      
          object ReadJsonGeneric<TItem>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
          {
              if (reader.TokenType == JsonToken.Null)
                  return null;
              var collection = (ICollection<TItem>)(existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
              if (reader.TokenType == JsonToken.StartArray)
                  serializer.Populate(reader, collection);
              else
                  collection.Add(serializer.Deserialize<TItem>(reader));
              return collection;
          }
      
          public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
          {
              if (reader.TokenType == JsonToken.Null)
                  return null;
              if (objectType.IsArray)
                  throw new JsonSerializationException("Read-only collections such as arrays are not supported");
              try
              {
                  var elementType = objectType.GetCollectItemTypes().SingleOrDefault();
                  if (elementType == null)
                      throw new JsonSerializationException(string.Format("{0} is not an ICollection<T> for some T", objectType));
                  var method = typeof(SingleOrArrayConverter).GetMethod("ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                  return method.MakeGenericMethod(new[] { elementType }).Invoke(this, new object[] { reader, objectType, existingValue, serializer });
              }
              catch (Exception ex)
              {
                  // Wrap the TargetInvocationException in a JsonSerializerException
                  throw new JsonSerializationException("Failed to deserialize " + objectType, ex);
              }
          }
      
          void WriteJsonGeneric<TItem>(JsonWriter writer, object value, JsonSerializer serializer)
          {
              var list = (ICollection<TItem>)value;
              if (list.Count == 1)
              {
                  foreach (object item in list)
                  {
                      serializer.Serialize(writer, item);
                      break;
                  }
              }
              else
              {
                  writer.WriteStartArray();
                  foreach (var item in list)
                  {
                      serializer.Serialize(writer, item);
                  }
                  writer.WriteEndArray();
              }
          }
      
          public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
          {
              var objectType = value.GetType();
              try
              {
                  var elementType = objectType.GetCollectItemTypes().SingleOrDefault();
                  if (elementType == null)
                      throw new JsonSerializationException(string.Format("{0} is not an ICollection<T> for some T", objectType));
                  var method = typeof(SingleOrArrayConverter).GetMethod("WriteJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                  method.MakeGenericMethod(new[] { elementType }).Invoke(this, new object[] { writer, value, serializer });
              }
              catch (Exception ex)
              {
                  // Wrap the TargetInvocationException in a JsonSerializerException
                  throw new JsonSerializationException("Failed to serialize " + objectType, ex);
              }
          }
      }
      
      public static class TypeExtensions
      {
          public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
          {
              if (type == null)
                  throw new ArgumentNullException();
              if (type.IsInterface)
                  return new[] { type }.Concat(type.GetInterfaces());
              else
                  return type.GetInterfaces();
          }
      
          public static IEnumerable<Type> GetCollectItemTypes(this Type type)
          {
              foreach (Type intType in type.GetInterfacesAndSelf())
              {
                  if (intType.IsGenericType
                      && intType.GetGenericTypeDefinition() == typeof(ICollection<>))
                  {
                      yield return intType.GetGenericArguments()[0];
                  }
              }
          }
      }
      
      public class StringConverterDecorator : JsonConverterDecorator
      {
          public StringConverterDecorator(Type jsonConverterType) : base(jsonConverterType) { }
      
          public StringConverterDecorator(JsonConverter converter) : base(converter) { }
      
          public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
          {
              if (reader.TokenType == JsonToken.Null)
                  return null;
              // Unwrap the double-serialized string.
              var s = JToken.Load(reader).ToString();
              var token = JToken.Parse(s);
              // Then convert the revealed JSON to its final form.
              using (var subReader = token.CreateReader())
              {
                  while (subReader.TokenType == JsonToken.None)
                      subReader.Read();
                  return base.ReadJson(subReader, objectType, existingValue, serializer);
              }
          }
      
          public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
          {
              string s;
      
              // Serialize the value to an intermediate JSON string.
              using (var textWriter = new StringWriter())
              {
                  using (var subWriter = new JsonTextWriter(textWriter))
                  {
                      base.WriteJson(subWriter, value, serializer);
                  }
                  s = textWriter.ToString();
              }
              // Then double-serialize the value by writing the JSON as a string literal to the output stream.
              writer.WriteValue(s);
          }
      }
      
      public abstract class JsonConverterDecorator : JsonConverter
      {
          readonly JsonConverter converter;
      
          public JsonConverterDecorator(Type jsonConverterType) : this((JsonConverter)Activator.CreateInstance(jsonConverterType)) { }
      
          public JsonConverterDecorator(JsonConverter converter)
          {
              if (converter == null)
                  throw new ArgumentNullException();
              this.converter = converter;
          }
      
          public override bool CanConvert(Type objectType)
          {
              return converter.CanConvert(objectType);
          }
      
          public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
          {
              return converter.ReadJson(reader, objectType, existingValue, serializer);
          }
      
          public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
          {
              converter.WriteJson(writer, value, serializer);
          }
      
          public override bool CanRead { get { return converter.CanRead; } }
      
          public override bool CanWrite { get { return converter.CanWrite; } }
      }
      

      然后使用[JsonConverter(typeof(TConverter), ...)]属性将链式转换器应用于Message属性,如下所示:

      public class Requirments
      {
          public int FileID { get; set; }
          public string EventMessageUId { get; set; }
          public string ProjectId { get; set; }
      
          [JsonConverter(typeof(StringConverterDecorator), typeof(SingleOrArrayConverter))]
          public List<Message> Message { get; set; }
      
          public string error { get; set; }
      }
      

      然后使用JsonConvert.DeserializeObject<T>反序列化:

      var requirement = JsonConvert.DeserializeObject<Requirments>(jsonString);
      

      或者,如果您不想将转换器直接应用于您的类型,可以将其添加到JsonSerializerSettings.Converters并反序列化,如下所示:

      var settings = new JsonSerializerSettings
      {
          Converters = { new StringConverterDecorator(new SingleOrArrayConverter<List<Message>, Message>()) },
      };
      var requirement = JsonConvert.DeserializeObject<Requirments>(json, settings);
      

      请注意,此处需要通用SingleOrArrayConverter<List<Message>, Message>以防止转换器应用于所有类型的集合。

      示例fiddle

答案 2 :(得分:0)

Message类的定义是对的。但是message属性的Json主体不是数组。所以课程应该是

public class Requirments
{

    public int FileID { get; set; }
    public string EventMessageUId { get; set; }
    public string ProjectId { get; set; }
    public Message Message { get; set; }
    //public object[] Message { get; set; }
    public string error { get; set; }
}

答案 3 :(得分:0)

主要问题在于你的JSON,它应该是这样的

{"EventMessageUId":"ef51b5a3-32b2-e611-baf9-fc3fdb446bd2","Message":
[{"StoryId":20500,"StoryDesc":"Test Story
data"}],"ProjectId":"1"}

之后,您将获得“消息”列表,并且您可以轻松地将其映射到班级。

public class Message
{
    public int StoryId { get; set; }
    public string StoryDesc { get; set; }
}

public class Requirments
{
    public string EventMessageUId { get; set; }
    public List<Message> Message { get; set; }
    public string ProjectId { get; set; }
}

答案 4 :(得分:0)

我已经成功地从您的Json中解析了班级类型消息的列表,但为此您需要更改您的班级要求

public class Requirments
    {    
        public int FileID { get; set; }
        public string EventMessageUId { get; set; }
        public string ProjectId { get; set; }
        public string  Message { get; set; } 
        //public List<Message> Message { get; set; } // **need to change property type to "string"**
        //public object[] Message { get; set; }
        public string error { get; set; }
    }

您可以尝试以下代码:

Requirments mainResult = JsonConvert.DeserializeObject<Requirments>("YOUR JSON STING");
List<Message> MessageList = JsonConvert.DeserializeObject<List<Message>>(mainResult.Message.ToString());

注意:您需要在班级中使用使用Newtonsoft.Json;

这将为您提供消息列表

中的课程类型列表消息

希望,这会有所帮助!