Объект для разделения коллекций KeyValue Pair - PullRequest
0 голосов
/ 04 апреля 2020

У меня есть проблема, когда мне нужно взять любой объект и сгладить его в формате пары ключ-значение

Это действительно хорошо работает для простых классов и даже классов, в которых у меня есть другие классы

Глядя на пример,

public class Buyer
{
    [JsonProperty("name")]public string Name { get; set; }
    [JsonProperty("address")]public string Address { get; set; }        
    [JsonProperty("lastPurchase")]public Purchase LastPurchase { get; set; }

    public Buyer()
    {
        Name = "Joe Bloggs";
        Address = "An adddress somewhere";

        AllPurchases = new List<Purchase>()
        {
            new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2017-01-01")},
            new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")}
        };

        LastPurchase = new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")};
    }

    [JsonIgnore]
    public List<Purchase> AllPurchases { get; set; }

}

public class Purchase
{
    public DateTime PurchaseDateTime { get; set; }
    public double PurchaseAmount { get; set; }
}

У меня есть код ниже, который является моей текущей реализацией

var buyer = new Buyer();
var json = JsonConvert.SerializeObject(buyer);
var obj = JObject.Parse(json);

var result = obj.Descendants()
            .OfType<JProperty>()
            .Where(s => s.Value.Type != JTokenType.Object)
            .Select(p => new KeyValuePair<string, string>(p.Path,
                p.Value.Type == JTokenType.Array || p.Value.Type == JTokenType.Object
                    ? null : p.Value.ToString()));


var serializerSettings = new JsonSerializerSettings
                         {
                             Formatting = Formatting.Indented,
                             ContractResolver = new CamelCasePropertyNamesContractResolver(),
                         };
var newJson = JsonConvert.SerializeObject(result, serializerSettings);
Console.WriteLine(newJson);

Это генерирует Json ниже, что идеально

[
  {
    "key": "name",
    "value": "Joe Bloggs"
  },
  {
    "key": "address",
    "value": "An adddress somewhere"
  },
  {
    "key": "lastPurchase.PurchaseDateTime",
    "value": "01/01/2018 00:00:00"
  },
  {
    "key": "lastPurchase.PurchaseAmount",
    "value": "100"
  }
]

Ситуация усложняется, когда я представляю сериализацию списка, удаляя JsonIgnore

Теперь я получаю

[
  {
    "key": "name",
    "value": "Joe Bloggs"
  },
  {
    "key": "address",
    "value": "An adddress somewhere"
  },
  {
    "key": "lastPurchase.PurchaseDateTime",
    "value": "01/01/2018 00:00:00"
  },
  {
    "key": "lastPurchase.PurchaseAmount",
    "value": "100"
  },
  {
    "key": "allPurchases",
    "value": null
  },
  {
    "key": "allPurchases[0].PurchaseDateTime",
    "value": "01/01/2017 00:00:00"
  },
  {
    "key": "allPurchases[0].PurchaseAmount",
    "value": "100"
  },
  {
    "key": "allPurchases[1].PurchaseDateTime",
    "value": "01/01/2018 00:00:00"
  },
  {
    "key": "allPurchases[1].PurchaseAmount",
    "value": "100"
  }
]

Это, очевидно, произошло из-за того, что в моей логике c нет ничего конкретного c в нем для обработки списков

Как я могу изменить свои логи c, чтобы AllPurchases был набором пар ключ-значение с ключом allPurchases [0], allPurchases [1], а значение - отдельным ключом сбор значений, который позволил бы избежать имен ключей, таких как allPurchases [0] .PurchaseAmount et c?

Мне нужно оставить решение универсальным c, чтобы оно сгладило любой объект в эту структуру

Пол

1 Ответ

0 голосов
/ 04 апреля 2020

Из того, что я вижу, вы хотите следующее. Значения и объекты, не входящие в массив, должны быть переданы json объектам в форме {key: "propertyPath", value: "valueTostring"} Sub Objects для массивов пар ключ-значение. Массивы для индексированных {key: "property [index]", value: "valueTostringOrObjectKeyValueArray"} Следующее

        var result = GetItmes(obj);


        IEnumerable<KeyValuePair<string,object>> GetItmes(in JToken token, string path = "")
        {
            return token switch
            {
                JObject jObject => from prop in token.Children<JProperty>()
                                   from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
                                   select child,
                JArray jArray => from item in jArray.Select((t, i) => (t, i))
                                 select new KeyValuePair<string, object>($"{path}[{item.i}]",GetItmes(item.t)),
                JValue jValue => new[] { 
                    new KeyValuePair<string, object>(path, (object)jValue?.ToString()) 
                },
                _ => Enumerable.Empty<KeyValuePair<string, object>>(),
            };
        }

Создает

    [
  {
    "key": "name",
    "value": "Joe Bloggs"
  },
  {
    "key": "address",
    "value": "An adddress somewhere"
  },
  {
    "key": "lastPurchase.PurchaseDateTime",
    "value": "1/1/2018 12:00:00 AM"
  },
  {
    "key": "lastPurchase.PurchaseAmount",
    "value": "100"
  },
  {
    "key": "AllPurchases[0]",
    "value": [
      {
        "key": "PurchaseDateTime",
        "value": "1/1/2017 12:00:00 AM"
      },
      {
        "key": "PurchaseAmount",
        "value": "100"
      }
    ]
  },
  {
    "key": "AllPurchases[1]",
    "value": [
      {
        "key": "PurchaseDateTime",
        "value": "1/1/2018 12:00:00 AM"
      },
      {
        "key": "PurchaseAmount",
        "value": "100"
      }
    ]
  }
]

Этот код рекурсивен и неоптимизирован. Я уверен может быть гораздо более эффективный способ сделать это.

Для

IEnumerable<KeyValuePair<string, object>> GetItmes(JToken token, string path = "")
        {
            switch (token)
            {
                case JObject jObject:
                    return from prop in token.Children<JProperty>()
                           from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
                           select child;
                case JArray jArray:
                    return from item in jArray.Select((t, i) => (t, i))
                           select new KeyValuePair<string, object>($"{path}[{item.i}]", GetItmes(item.t));
                case JValue jValue:
                    return new[] {
                    new KeyValuePair<string, object>(path, (object)jValue?.ToString())
                };
                default: return Enumerable.Empty<KeyValuePair<string, object>>();
            };
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...