Имя первого свойства в JSON

#c# #json #dynamic #json.net

#c# #json #динамический #json.net

Вопрос:

Я запрашиваю конечную точку REST для получения данных. Результатом может быть один из двух видов документов

В упрощенных версиях это

1.

 {
    "Feature": {
        "CreationDate": "2018-07-12T09:22:37.068Z",
        "_CreatedAt": "Jul 12, 2018",
        "ObjectID": 236769828012,
        "ObjectUUID": "5a4fa66b-a81b-48c3-afdc-b7ad8c4d4d1b",
        "VersionId": "36",
        }
}
  
 {
    "Initiative": {
        "CreationDate": "2018-07-12T09:22:37.068Z",
        "_CreatedAt": "Jul 12, 2018",
        "ObjectID": 236769828012,
        "ObjectUUID": "5a4fa66b-a81b-48c3-afdc-b7ad8c4d4d1b",
        "VersionId": "36",
        }
}
  

Таким образом, структура документов идентична — кроме первого свойства — чего я сейчас не делаю во время выполнения.

Мой вопрос:

Как мне получить значение для этого свойства?

Мой код для извлечения просто использует компонент Newtonsoft для десериализации результата из API в dynamic конструкцию.

 static void GetItemByProject(string projectId)
{
    //var uri = ...;
    response = GetData(uri);
    dynamic itemData = JsonConvert.DeserializeObject(response.Result);
}

static async Task<string> GetData(string uri) 
{
    return await client.GetStringAsync(uri);
}

static readonly HttpClient client = new HttpClient();
  

Я знаю, что могу использовать отражение или преобразовать результат метода DeserializeObject в Dictionary<string, dynamic> результат и перебирать ключи, но это кажется действительно громоздким.

Есть ли более элегантный способ сделать то, что я хочу?

Комментарии:

1. Просто FWIW, вам бы не повезло, если бы было второе свойство. JSON описывает объекты следующим образом: «Объект представляет собой неупорядоченный набор пар имя / значение». (JavaScript, с другой стороны, привнес в них порядок. JSON все еще не имеет и, вероятно, не будет.) Но поскольку вы имеете дело только с одним свойством, вы в хорошей форме. 🙂

2. @T.J.Crowder да, я знаю… но в моем случае я на 100% уверен, что второго свойства никогда не будет 🙂

3. Вы можете использовать JObject и Json.Linq

4. Если вам нужно иметь дело только с двумя именами свойств, у вас, вероятно, может быть класс-оболочка, в котором определены оба свойства. При десериализации только одно из двух свойств будет иметь значение. Вы также можете сделать два свойства закрытыми и добавить [JsonProperty] к обоим, а затем предоставить ненулевое свойство через общедоступное свойство. Это быстро реализуется, не уверен, что это элегантно .

Ответ №1:

Вы можете использовать Json.Linq для этого просто получить первое свойство и десериализовать его значение

 var result = JObject.Parse(response.Result);
var value = result.Properties().FirstOrDefault()?.Value.ToObject<Response>();
  

Где Response класс может быть следующим (просто пример, вы можете использовать любую модель, которую хотите)

 public class Response
{
    public DateTime CreationDate { get; set; }
    public string _CreatedAt { get; set; }
    public long ObjectID { get; set; }
    public string ObjectUUID { get; set; }
    public string VersionId { get; set; }
}
  

Комментарии:

1. @JesperLundStocholm Отвечает ли это на ваш вопрос, вам нужны более подробные сведения или пояснения?

2. Привет, Павел, как упоминалось @Roman ниже, я хочу избежать создания классов-оболочек для обработки этого. Я ищу решение для прямой работы с ответом JSON 🙂

3. @JesperLundStocholm Что мешает вам использовать JObject свойства и значения напрямую?

4. ничего … Мне нужно будет заглянуть в JObject, чтобы узнать, работает ли он для меня, так что спасибо за это предложение. Я просто хочу использовать его без необходимости поддерживать классы-оболочки в качестве класса «Response» выше 🙂

Ответ №2:

Предполагая, что мы имеем

 public class Root
{
    public FeatureOrInitiative Feature { get; set; }
    public FeatureOrInitiative Initiative { get; set; }

    public static Root FromJson(string json) => JsonConvert.DeserializeObject<Root>(json);
}

public class FeatureOrInitiative
{
    [JsonProperty("CreationDate")]
    public DateTimeOffset CreationDate { get; set; }

    [JsonProperty("_CreatedAt")]
    public string CreatedAt { get; set; }

    [JsonProperty("ObjectID")]
    public long ObjectId { get; set; }

    [JsonProperty("ObjectUUID")]
    public Guid ObjectUuid { get; set; }

    [JsonProperty("VersionId")]
    public long VersionId { get; set; }
}
  

На самом деле это оно. Мы просто вызываем наш десериализатор и получаем то, что нам нужно для обоих вариантов:

 Root itemData = Root.FromJson(response.Result);
// Feature or Initiative is null
  

Комментарии:

1. Привет @Roman, я намеренно старался избегать использования C # DTO (классов-оболочек) для обработки этого. На данный момент я не знаю, какие свойства мне нужны, и не хочу также поддерживать класс-оболочку.