【问题标题】:JSON property name as whole JSON path to the propertyJSON 属性名称作为属性的整个 JSON 路径
【发布时间】:2021-03-11 11:25:07
【问题描述】:

我希望每个 JSON 属性名称都代表它的路径。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string StreetNumber { get; set; }
}

使用 Newtonsoft.Json 我目前得到这个 JSON:

{
   "firstName":"Homer",
   "lastName":"Simpson",
   "address":{
      "street":"Evergreen Terrace",
      "streetNumber":"742"
   }
}

是否可以改为获取此 JSON:

{
   "person.firstName":"Homer",
   "person.lastName":"Simpson",
   "person.address":{
      "person.address.street":"Evergreen Terrace",
      "person.address.streetNumber":"742"
   }
}

【问题讨论】:

  • 之后您需要对 JSON 进行后处理。 Json.NET 是一个基于合约的序列化器,因此如果图中出现多个Address 对象,它将使用相同的合约以相同的方式序列化它们。对于图中每次出现的Address,您需要不同的合同(因为不同的属性名称)。序列化到JObject 并在之后修复属性名称似乎是要走的路。这能回答你的问题吗?

标签: c# json .net json.net


【解决方案1】:

你不能这样做,因为它是模棱两可的。这个文件如何反序列化?

在 C# 中,它可以被解释为...

public class Person
{
    public string Person.firstName { get; set; }
    public string Person.LastName { get; set; }
    public Address Person.Address { get; set; }
}

这是一种无效的格式,所以你的文件一定会导致异常或编译错误。

在浏览器上试试这个,使用JSON.parse() 方法,你会得到一个具有这种结构的对象(属性名称带有一个点)。

【讨论】:

    【解决方案2】:

    此格式无效。但 : 样本

            private static string _getCustomJsonFormat(object o, string _parentPath = "")
        {
            string str = "{" ;
            {
    
                var t = o.GetType();
                var props = t.GetProperties();
                if (props != null)
                {
                    List<string> _kv = new List<string>();
                    foreach (var i in props)
                    {
                        List<string> _path = new List<string>() { };
                        if (_parentPath.Length == 0) _path.Add(t.Name);
                        _path.AddRange(_parentPath.Split('.').Select(m => m.Trim()).Where(m => m.Length > 0));
                        string _p = i.Name; 
                        object _v = i.GetValue(o);
                        Type _vt = _v.GetType();
                        _path.Add(_p);
                        if (_vt.IsNested)
                        {
                            // Get Object
                            _kv.Add("\"" + string.Join(".",_path) + "\":" + _getCustomJsonFormat(_v, string.Join(".", _path)));
                        }
                        else {
                            // Get Property
                            _kv.Add("\"" + string.Join(".", _path)+ "\":\"" + _v.ToString() + "\"");
                        }
                    }
                    str += string.Join(",", _kv);
    
                }
            }
            return str+" }";
        }
    

    使用:

                var p = new Person() { Address = new Address() { Street = "1", StreetNumber = "2" }, FirstName = "a", LastName = "b" };
            var x = _getCustomJsonFormat(p);
    

    【讨论】:

      【解决方案3】:

      Json.NET 不会使用 JSONPaths 作为开箱即用的属性名称来序列化对象,因为它是基于协定的序列化程序。因此,它将以相同的方式序列化某种类型的所有实例T,无论它们在序列化图中遇到什么。相反,您希望为遇到的每个实例创建一个稍微不同的合约,以反映图中实例的路径。

      那么,您有哪些选择来获取所需的 JSON?

      首先,您可以序列化为JObject,然后使用public static void Rename(this JToken token, string newName) 扩展方法将其从this answerBrian Rogers 后处理到Rename JProperty in json.net

      var person = new Person
      {
          // Initialize your Person as required
      };
      
      var settings = new JsonSerializerSettings
      {
          ContractResolver = new CamelCasePropertyNamesContractResolver(),
      };
      var jObject = JObject.FromObject(person, JsonSerializer.CreateDefault(settings));
      foreach (var item in jObject.Descendants().OfType<JProperty>().Select(p => (Property: p, p.Path)).ToList())
      {
          // The Rename extension method from https://stackoverflow.com/a/47269811/3744182
          item.Property.Rename("person." + item.Path);
      }
      
      var json = jObject.ToString();
      

      演示小提琴 #1 here.

      其次,您可以在序列化时通过继承JsonTextWriter 并覆盖WritePropertyName() 方法来动态重命名属性:

      public class NameRemappingJsonWriter : JsonTextWriter
      {
          readonly Action<string> jsonWriterWritePropertyName;
          readonly Func<string, int, string, string> map;
      
          public NameRemappingJsonWriter(TextWriter textWriter, Func<string, int, string, string> map) : base(textWriter)
          {
              this.map = map ?? throw new ArgumentNullException(nameof(map));
              //Method to call a base-of-base-class method taken from this answer https://stackoverflow.com/a/32562464
              //By https://stackoverflow.com/users/5311735/evk
              //To https://stackoverflow.com/questions/2323401/how-to-call-base-base-method
              var ptr = typeof(JsonWriter).GetMethod("WritePropertyName", new[] { typeof(string) }).MethodHandle.GetFunctionPointer();
              jsonWriterWritePropertyName = (Action<string>)Activator.CreateInstance(typeof(Action<string>), this, ptr);
          }
      
          public override void WritePropertyName(string name) => WritePropertyName(name, true);
      
          public override void WritePropertyName(string name, bool escape)
          {
              jsonWriterWritePropertyName(name);
              WriteRaw(JsonConvert.ToString(map(name, Top, Path)));
              WriteRaw(":");
          }       
      }
      

      然后:

      var settings = new JsonSerializerSettings
      {
          ContractResolver = new CamelCasePropertyNamesContractResolver(),
      };
      Func<string, int, string, string> map = (name, depth, parentPath) => "person." + parentPath;
      using var textWriter = new StringWriter();
      using (var jsonWriter = new NameRemappingJsonWriter(textWriter, map) { Formatting = Formatting.Indented })
      {
          JsonSerializer.CreateDefault(settings).Serialize(jsonWriter, person);
      }
      var json = textWriter.ToString();
      

      请注意,要让它工作,我必须调用基类的基类 JsonWriter.WritePropertyName() 方法,这有点粗略和 not recommended or supported natively in c#

      演示小提琴 #2 here.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-04
        • 1970-01-01
        • 1970-01-01
        • 2021-10-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多