You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何更优解析API返回的不一致JSON持仓数据结构?

处理Web服务返回JSON结构不一致的问题

对接的Web服务positions端点GET请求返回的JSON存在结构不一致:

  • 多持仓场景:positions字段包含position数组
  • 单持仓场景:positions字段直接是单个持仓对象

已实现基础解析逻辑,以下是行业通用的反序列化技巧及POCO落地方案:

示例返回结构

多持仓返回

{
  "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"
      }
    ]
  }
}

单持仓返回

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

当前解析代码

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>>();
        }
    }
}

通用反序列化技巧与POCO方案

1. 自定义JsonConverter(Newtonsoft.Json)

核心思路是写一个通用转换器,自动识别字段是单个对象还是数组,统一转为集合类型,避免手动判断JToken类型。

步骤1:定义POCO类

public class BrokerPosition
{
    [JsonProperty("cost_basis")]
    public decimal CostBasis { get; set; }

    [JsonProperty("date_acquired")]
    public DateTime DateAcquired { get; set; }

    public int Id { get; set; }

    public decimal Quantity { get; set; }

    public string Symbol { get; set; }
}

public class PositionsWrapper
{
    [JsonProperty("positions")]
    [JsonConverter(typeof(SingleOrArrayConverter<BrokerPosition>))]
    public List<BrokerPosition> Positions { get; set; }
}

步骤2:实现通用转换器

public class SingleOrArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(List<>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        // 处理嵌套的position字段(多持仓场景)
        if (token.Type == JTokenType.Object && token["position"] != null)
        {
            token = token["position"];
        }
        
        if (token.Type == JTokenType.Array)
        {
            return token.ToObject<List<T>>();
        }
        // 单持仓场景直接转成单个元素的集合
        return new List<T> { token.ToObject<T>() };
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // 如果不需要序列化回这种结构,可留空或抛出异常
        throw new NotImplementedException("此转换器仅用于反序列化");
    }
}

步骤3:简化后的解析代码

using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"v1/accounts/{tradierHost.TradierAccount}/positions"))
using (HttpResponseMessage response = await this.tradierClient.SendAsync(httpRequestMessage))
{
    response.EnsureSuccessStatusCode();
    var jsonContent = await response.Content.ReadAsStringAsync();
    
    // 直接反序列化为POCO,转换器自动处理单/多持仓逻辑
    var result = JsonConvert.DeserializeObject<PositionsWrapper>(jsonContent, new SingleOrArrayConverter<BrokerPosition>());
    IEnumerable<BrokerPosition> brokerPositions = result.Positions;
}

2. 其他替代方案

  • System.Text.Json的自定义转换器:如果使用.NET Core/.NET 5+的System.Text.Json,可实现JsonConverter<List<T>>,逻辑和Newtonsoft.Json版本类似。
  • 预处理JSON字符串:在反序列化前,把单个对象包装成数组(比如替换"positions":{...}为"positions":{"position":[...]}),但这种方式不够优雅,维护性差,不推荐。

内容的提问来源于stack exchange,提问作者user365462

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:12:39