Home > Back-end >  Is there a better way to parse this JSON structure?
Is there a better way to parse this JSON structure?

Time:07-18

I'm dealing with a web service that defines the following JSON structure as the return for a GET to the 'positions' endpoint:

{
  "positions": {
    "position": [
      {
        "cost_basis": 207.01,
        "date_acquired": "2018-08-08T14:41:11.405Z",
        "id": 130089,
        "quantity": 1.00000000,
        "symbol": "AAPL"
      },
      {
        "cost_basis": 1870.70,
        "date_acquired": "2018-08-08T14:42:00.774Z",
        "id": 130090,
        "quantity": 1.00000000,
        "symbol": "AMZN"
      },

Which is all find and good, except that when there's just one position, they return:

{
  "positions":
  {
    "cost_basis": 1870.70,
    "date_acquired": "2018-08-08T14:42:00.774Z",
    "id": 130090,
    "quantity": 1.00000000,
    "symbol": "AMZN"
  }
}

Is there some industry trick to deserializing this from JSON? The best I've come up with is:

using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"v1/accounts/{tradierHost.TradierAccount}/positions"))
using (HttpResponseMessage response = await this.tradierClient.SendAsync(httpRequestMessage))
{
    // Make sure we executed with no errors.
    response.EnsureSuccessStatusCode();

    var jObject = JObject.Parse(await response.Content.ReadAsStringAsync());
    IEnumerable<BrokerPosition> brokerPositions = null;
    if (jObject["positions"] is JObject positionsObject)
    {
        // Update one position.
        if (positionsObject["position"] is JObject positionObject)
        {
            var brokerPositionList = new List<BrokerPosition>();
            brokerPositionList.Add(positionObject.ToObject<BrokerPosition>());
        }

        // Update many positions.
        if (positionsObject["position"] is JArray positionArray)
        {
            brokerPositions = positionArray.ToObject<IEnumerable<BrokerPosition>>();
        }
    }
}

Is there a better way to parse this vendor's API? I don't see a practical way to use a POCO.

CodePudding user response:

I prefer to use a constructor

    Data data = JsonConvert.DeserializeObject<Data>(json);

classes

public class Data
{
    public Positions Positions { get; set; } = new Positions();

    [Newtonsoft.Json.JsonConstructor]
    public Data(JToken positions)
    {
        var positionsObj = JObject.FromObject(positions);
        if (positions["position"] != null)
            Positions.Position = positionsObj["position"].ToObject<List<Position>>();

        else
        {
            Positions.Position = new List<Position>();
            Positions.Position.Add(positionsObj.ToObject<Position>());
        }
    }
}

public class Positions
{
    public List<Position> Position { get; set; }
}
public class Position
{
    public double cost_basis { get; set; }
    public DateTime date_acquired { get; set; }
    public int id { get; set; }
    public double quantity { get; set; }
    public string symbol { get; set; }
}

or if you don't need the whole object, it can be much simplier. You only need one class

    var positionsObj = JObject.Parse(json)["positions"];
 
    List<Position> positions = new List<Position>();

    if (positionsObj["position"] != null)
        positions = positionsObj["position"].ToObject<List<Position>>();
    else
        positions.Add(positionsObj.ToObject<Position>());

CodePudding user response:

You can define your class model to use it to parse JSON in the model.And use System.Text.Json or Newtonsoft.Json Like example:

var responseModel = JsonSerializer.Deserialize<YourReponseModel>(response.Content.ReadAsStringAsync());

So you can get access to Position by object property: responseModel.Positions

  • Related