【问题标题】:How to define string to object/dictionary in JSON如何在 JSON 中将字符串定义为对象/字典
【发布时间】:2018-04-11 05:06:53
【问题描述】:

我创建了一个类来定义我的请求,我没有得到接受的 JSON 字符串

我定义了这个对象:

public class Request
{
    public Var_Args[] var_args { get; set; }
}

public class  Var_Args
{
    public object title { get; set; }
    public object owner { get; set; }
}

当我将其转换为 json 时,我得到以下字符串:

{"requests":[{"var_args":[{"title":"Test","owner":"skaner"}]}]}

如何定义类,以获得接受的 json 字符串:

{"requests":[{"var_args":[{"title":"Test"},{"owner":"skaner"}]}]}

【问题讨论】:

  • 您的示例中是否存在源代码错误:{"requests":[{""var_args":[{"title":"Test"},{"owner":"skaner"} ]}]} 应该是 {"requests":[{"var_args":[{"title":"Test"},{"owner":"skaner"}]}]}
  • 当我发送第一个 json 字符串时,我收到错误:{ "responses": [ { "tran_id": "unknown", "UUID": "", "status": "error", "messages": [ { "errorcode": 20001, "errorsummary": "Incorrect JSON Format", "errordetail": "Incorrect JSON Format" } ], "result_params": {}, "result_table": [] } ] }
  • 这是我的错误,我没有正确复制代码。修复,我还有一个问题...
  • 将 JsonPropertyAttribute 添加到您的属性中,您可以定义任何您想要的名称。
  • 你在使用 json.net 吗?

标签: c# json


【解决方案1】:

您可以编写自定义 JSON 转换器,将对象(已知类型)的每个属性序列化为不同的 JSON 对象。

public class PropertyAsObjectConverter : JsonConverter
{
   private readonly Type[] _types;

   public PropertyAsObjectConverter(params Type[] types)
   {
      _types = types;
   }

   public override bool CanConvert(Type objectType)
   {
      return _types.Any(t => t == objectType);
   }

   public override bool CanRead
   {
      get { return false; }
   }

   public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
   {
      throw new NotImplementedException();
   }

   public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
   {
      var properties = value.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance);
      foreach(var property in properties)
      {
         var name = property.Name;
         var attrs = property.GetCustomAttributes(typeof(JsonPropertyAttribute));
         if(attrs != null)
         {
            if (attrs.FirstOrDefault() is JsonPropertyAttribute attr)
               name = attr.PropertyName;
         }

         writer.WriteStartObject();
         writer.WritePropertyName(name);
         serializer.Serialize(writer, property.GetValue(value));
         writer.WriteEndObject();
      }
   }
}

这仅实现了序列化,但您也可以对其进行扩展以支持反序列化。如果需要,您还可以将其扩展为序列化字段。

然后您可以如下定义您的类。请注意,我在这里使用JsonPropertyAttribute 来指定序列化JSON 中的名称。

public class Content
{
   [JsonProperty("requests")]
   public Request Value { get; set; }
}

public class Request
{
   [JsonProperty("var_args")]
   public VarArgs[] Arguments { get; set; }
}

public class VarArgs
{
   [JsonProperty("title")]
   public object Title { get; set; }

   [JsonProperty("owner")]
   public object Owner { get; set; }
}

你可以这样使用它:

static void Main(string[] args)
{
   var request = new Content()
   {
      Value = new Request()
      {
         Arguments = new VarArgs[]
         {
            new VarArgs()
            {
               Title = "Test",
               Owner = "Skaner",
            }
         }
      }
   };

   var text = JsonConvert.SerializeObject(
      request,
      Formatting.None, 
      new PropertyAsObjectConverter(typeof(VarArgs)));

   Console.WriteLine(text);
}

此示例的输出是您所期望的:

{"requests":{"var_args":[{"title":"Test"},{"owner":"Skaner"}]}}

【讨论】:

    【解决方案2】:

    您可以使用custom JsonConverter,如下所示。 它获取Var_Args 对象并将其拆分为两个不同的JObject,这两个JObject 对应于两个不同的JSON 对象。

    public class VarArgsConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var obj = (JObject)JToken.FromObject(value);
    
            var objTitle = new JObject();
            objTitle.Add("title", obj.GetValue("title"));
    
            var objOwner = new JObject();
            objOwner.Add("owner", obj.GetValue("owner"));
    
            objTitle.WriteTo(writer);
            objOwner.WriteTo(writer);
        }
    
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
        }
    
        public override bool CanRead
        {
            get { return false; }
        }
    
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(Var_Args);
        }
    }
    
    public class Wrapper
    {
        [JsonProperty("requests")]
        public Request Requests { get; set; }
    }
    
    public class Request
    {
        public Var_Args[] var_args { get; set; }
    }
    
    public class Var_Args
    {
        public object title { get; set; }
        public object owner { get; set; }
    }
    

    然后使用它:

    var wrapper = new Wrapper();
    
    var request = new Request();
    request.var_args = new Var_Args[] {
        new Var_Args(){ title = "Test", owner = "skaner" },
        new Var_Args(){ title = "Test2", owner = "skaner2" }
    };
    
    wrapper.Requests = request;
    
    var serialized = JsonConvert.SerializeObject(wrapper, new VarArgsConverter());
    

    输出

    {"requests":{"var_args":[{"title":"Test"},{"owner":"skaner"},{"title":"Test2"},{"owner":"skaner2"}]}}
    

    注意:我使用 Wrapper 类只是为了生成请求的 JSON。

    如果您不想每次都指定转换器,您可以全局注册您的转换器。请参阅this answer,它解释了如何做到这一点。因此,每次您尝试序列化 Var_Args 对象时,序列化程序都会使用您的自定义 JsonConverter

    如果您在全球注册JsonConvert,您可以使用:

    var serialized = JsonConvert.SerializeObject(wrapper);
    

    【讨论】:

      【解决方案3】:

      您可以使用System.ReflectionVar_Args 重新定义为IEnumerable<Dictionary<string,object>> 接口的实现,方法是在类中添加两个方法:

      public class Var_Args : IEnumerable<Dictionary<string,object>>
      {
          IEnumerator IEnumerable.GetEnumerator()
          {
              return this.GetEnumerator();
          }
      
          public IEnumerator<Dictionary<string,object>> GetEnumerator()
          {
              var Properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
      
              foreach (var Property in Properties)
              {
                  var Entry = new Dictionary<string,object>();
                  Entry.Add(Property.Name, Property.GetValue(this));
                  yield return Entry;
              }
          }
      
          public object title { get; set; }
          public object owner { get; set; }
      }
      

      虽然Reflection 可能被认为很慢,但您可以使用一种技术在运行时静态编译IEnumerable,以便在定义类时只发生一次反射,如下所示:

      public class Var_Args : IEnumerable<Dictionary<string,object>>
      {
          private struct PropertyList<T>
          {
              public static readonly List<Func<T,Dictionary<string,object>>> PropertyGetters;
      
              static PropertyList()
              {
                  PropertyGetters = new List<Func<T,Dictionary<string,object>>>();
                  var Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
      
                  foreach (var Property in Properties)
                  {
                      var Args = new [] { Expression.Parameter(typeof(T)) };
                      var Key = Property.Name;
                      var Value = Expression.Property(Args[0], Property);
                      Func<T,object> Get = Expression.Lambda<Func<T,object>>(Value, Args).Compile();
      
                      PropertyGetters.Add(obj =>
                      {
                          var entry = new Dictionary<string,object>();
                          entry.Add(Key, Get(obj));
                          return entry;
                      });
                  }
              }
          }
      
          protected static IEnumerable<Dictionary<string,object>> GetPropertiesAsEntries<T>(T obj)
          {
              return PropertyList<T>.PropertyGetters.Select(f => f(obj));
          }
      
          IEnumerator IEnumerable.GetEnumerator()
          {
              return this.GetEnumerator();
          }
      
          public IEnumerator<Dictionary<string,object>> GetEnumerator()
          {
              return GetPropertiesAsEntries(this).GetEnumerator();
          }
      
          public object title { get; set; }
          public object owner { get; set; }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-13
        • 2013-12-10
        • 2023-04-01
        相关资源
        最近更新 更多