【问题标题】:Print object with nested properties as string of comma separated key-value pairs将具有嵌套属性的对象打印为逗号分隔的键值对字符串
【发布时间】:2018-10-12 13:39:52
【问题描述】:

我有一个Person 类:

public class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
}

作为最终结果,我想将 Person 实例打印为 JSON,但我希望它是一个巨大的键值对字符串,例如:

"Name:John,Surname:Doe,Line1:Infinite Loop,Line2:California"

请注意,在上面的示例中,我去掉了实际的类名(即它打印出 Line1 而不是 Address.Line1) - 我只关心所有名称/值对。

所以最终结果将是Persons 的数组:

"persons":[
    "Name:John,Surname:Doe,Line1:Infinite Loop 1,Line2:California",            
    "Name:Jane,Surname:Doe,Line1:Infinite Loop 2,Line2:California"
]

作为起点,我尝试使用反射:

void Main()
{
    var persons = new List<Person>();
    persons.Add(new Person
    {
        Name = "John",
        Surname = "Doe",
        Address = new Address
        {
            Line1 = "Infinite Loop",
            Line2 = "California"                
        }
    });

    foreach(var person in persons)
    {
        var properties = new List<string>();
        foreach(var property in person.GetType().GetProperties())
        {
            properties.Add($"{property.Name}:{property.GetValue(person, null)}");
        }
        Console.WriteLine(string.Join(",", properties));
    }
}

但我在 LINQPad 中得到以下输出:

Name:John,Surname:Doe,Address:UserQuery+Address

我假设Address 没有正确迭代,因为它是Person 中的嵌套对象。即便如此,这看起来也不是最干净/最有效的方法。

【问题讨论】:

  • 现在,您可以反映 UserQuery.Address 对象的属性以获取它们的值。也就是说,您为什么不使用“正确的”JSON 序列化,例如在 Json.NET 的帮助下? (在我看来你想重新发明轮子......)
  • @elgonzo 因为我只需要所有属性作为名称/值,由, 分隔。使用 JSON,Address 将成为Person 中的一个对象(即包裹在{ } 中),这是我无法做到的。如果有,你能给我指出正确的方向吗?
  • 创建一个只包含您想要序列化的属性的新类(通常称为 DTO),将您的复杂对象映射到 DTO 的集合中,然后让内置的序列化器完成它。
  • @ChrisPickford 也许我误解了一些东西,我想基本上展平对象,因此没有层次结构/嵌套,并将所有内容打印为键值对字符串。序列化程序不会尊重嵌套(这是我不想要的)吗?
  • 如果普通 JSON(反)序列化在您的场景中不可行,那么要么考虑 UserQuery.Address 对象(如我的第一条评论中所述),要么为 UserQuery.Address 配备@987654337 @覆盖,如下面的答案所示。 (但请重新考虑并验证您的假设/想法/想法/等等......这导致您得出结论,将 Peson 的 JSON 序列化为 JSON 对象对于您的用例是不可行的。毕竟,避免 JSON 序列化并构建一些自定义JSON文本表示应该有一个理由来证明你必须付出的努力......)

标签: c# .net reflection json.net


【解决方案1】:

您可以像这样使用自定义 JsonConverter 来做到这一点:

class PersonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Person);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var pairs = JObject.FromObject(value)
            .Descendants()
            .OfType<JProperty>()
            .Where(p => p.Value is JValue)
            .Select(p => p.Name + ":" + p.Value);
        writer.WriteValue(string.Join(",", pairs));
    }

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

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

然后通过将转换器传递给JsonConvert.SerializeObject 来使用它,如下所示:

string json = JsonConvert.SerializeObject(obj, Formatting.Indented, new PersonConverter());

这是一个工作演示:https://dotnetfiddle.net/L4YDsm

【讨论】:

    【解决方案2】:

    ToString 覆盖添加到您的类并以 JSON 形式返回字符串集合。

    public class Person
    {
      public string Name { get; set; }
      public string Surname { get; set; }
      public Address Address { get; set; }
      public override string ToString()
      {
        return $"Name:{Name},Surname:{Surname},Line1:{Address?.Line1},Line2:{Address?.Line2}";
      }
    }
    

    【讨论】:

    • 谢谢克里斯。我试图避免硬编码所有属性,实际上对象要大得多。
    【解决方案3】:

    您必须以一种可以深入到对象结构中的方式实现反射,以便执行您想要的操作。这是对您提供的代码的简单递归和通用改编。

    public static string GetRecursivePropertyValues(object obj)
    {
        var properties = new List<string>();
        foreach (var property in obj.GetType().GetProperties())
        {
            object currentPropertyValue = property.GetValue(obj);
            if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
                properties.Add($"{property.Name}:{currentPropertyValue}");
            else
            {
                var subProperties = GetRecursivePropertyValues(currentPropertyValue);
                properties.Add(subProperties);
            }
        }
        return string.Join(";", properties);
    }
    

    此实现首先验证每个属性是否使用原始类型或string(在 C# 中不被视为原始类型)定义,如果是这种情况,则正常打印它们。否则,如果类型是复杂的(例如,如果它被声明为 Address 实例),它会递归检索复杂对象的属性,并将它们添加到结果字符串中。

    你可以这样称呼它:

    string propertiesStr = GetRecursivePropertyValues(person);

    编辑:代码现在仅根据 OP 的要求展平对象(之前的代码示例使用了一些质朴的 JSON 格式)。

    【讨论】:

      【解决方案4】:

      使用 Newtonsoft.json

      https://www.newtonsoft.com/json/help/html/SerializingJSON.htm

      检查上面的链接...它会告诉你如何将一个对象序列化为 json,重要的是要注意,你需要将所有项目添加到一个列表中,然后将列表序列化为 json 以获得所需的效果

      Product product = new Product();
      
      product.Name = "Apple";
      product.ExpiryDate = new DateTime(2008, 12, 28);
      product.Price = 3.99M;
      product.Sizes = new string[] { "Small", "Medium", "Large" };
      

      有例子:

      string output = JsonConvert.SerializeObject(product);
      //{
      //  "Name": "Apple",
      //  "ExpiryDate": "2008-12-28T00:00:00",
      //  "Price": 3.99,
      //  "Sizes": [
      //    "Small",
      //    "Medium",
      //    "Large"
      //  ]
      //}
      

      【讨论】:

      • 这并没有真正回答有关如何将Person 属性转换为 JSON 内的裸键值对字符串的问题,包括子对象的属性。
      • 不,这不是即插即用的答案,就像我说的那样,op 必须做一些修改,但代码就在那里。我想我可以为他做这件事,但我不认为这就是我们来这里的原因......
      猜你喜欢
      • 2019-01-23
      • 1970-01-01
      • 2023-04-09
      • 2019-02-11
      • 2021-02-17
      • 1970-01-01
      • 2016-04-01
      • 1970-01-01
      • 2013-02-06
      相关资源
      最近更新 更多