【问题标题】:passing base object with inherited object value (Json)传递具有继承对象值的基础对象(Json)
【发布时间】:2026-02-14 22:25:04
【问题描述】:

我使用以下代码调用了一个 WebAPI:

var client = new HttpClient
{
    BaseAddress = new Uri("http://localhost:8490/")
};
var jObject = new JObject();
jObject.Add("paramA", paramA);
jObject.Add("paramB", paramB);
JArray jArr = JArray.FromObject(paramsGenericArr);
jObject.Add("paramC", jArr);
var content = new StringContent(jObject.ToString(), Encoding.UTF8, "application/json");
var result = await client.PostAsync("api/path/tofunc", content).ConfigureAwait(false);
result.EnsureSuccessStatusCode();

ParamsGeneric 类是具有 2 个派生类的抽象类型:

[DataContract]
public class ParamsTypeA : ParamsGeneric
{
    [DataMember]
    public long itemC {get; set;}
    public ParamsTypeA() :
                      base()
    {}
}

[DataContract]
public class ParamsTypeB : ParamsGeneric
{
    [DataMember]
    public long itemD {get; set;}
    public ParamsTypeB() :
                      base()
    {}
}

[DataContract]
[KnownType(typeof(ParamsTypeA))]
[KnownType(typeof(ParamsTypeB))]
public abstract class ParamsGeneric
{
    [DataMember]
    public long itemA { get; set; }
    [DataMember]
    public long itemB {get; set;}
    public ParamsGeneric() 
    {}
}

我怀疑我在 WebAPI 中的反序列化有问题:

public class ClientData
{
    public string paramA { get; set; }
    public string paramB { get; set; }
    public ParamsGeneric[] paramC { get; set; }
}

[HttpPost]
[Route("api/path/tofunc")]
public async Task<bool> DoStuffAsync(ClientData clientData)
{
    ....
}

我对@9​​87654325@/paramC(类型为ParamsGeneric[],并保存ParamsTypeAParamsTypeB 类型的项目)有疑问

WebAPI 接收一个空白数组 (ParamsGeneric[0]) 以及其他参数。

将获得帮助。

更新

即使我尝试传递单个 ParamsGeneric 对象而不是数组,我也会收到 null 而不是对象。

解决方案

var serializer = new JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.Auto;
JArray jArr = JArray.FromObject(paramsGenericArr, serializer);

成功了。

【问题讨论】:

  • 旁注,你知道你可以在 Json.Net 中使用匿名对象来创建 json 吗? JsonConvert.SerializeObject(new { paramA, paramB, paramC = new[] { ... } })
  • @LasseV.Karlsen 很高兴知道。
  • 查看this post 以获取用于处理继承类的自定义 JSON 转换器。该帖子还解释了为什么不建议使用TypeNameHandling.Auto or All

标签: c# serialization asp.net-web-api json.net


【解决方案1】:

虽然在消息/json 中继承绝对是可能的,恕我直言;太麻烦了:)

无论如何,您实际上可以让 Newtonsoft.Json 为您处理继承,方法是设置 TypeNameHandling

// Allow inheritance in json payload
JsonSerializerSettings serializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
serializerSettings.TypeNameHandling = TypeNameHandling.All;

.. 或者只是

serializerSettings.TypeNameHandling = TypeNameHandling.Auto;

.. 取决于您的需要。

这是一个简单的解决方法,如果它是一个内部 API,或者如果您可以保证您将始终控制客户端,它会很好地工作。如果您有外部客户端,我会采用“覆盖默认模型绑定器”的方法,例如此处发布的“Deserialising Json to derived types in Asp.Net Web API”,或者非常强烈地考虑避免在 API 模型中继承。

【讨论】:

    【解决方案2】:

    尝试将数据传递给您的 API,如下所示

    Dictionary<string, string> param = new Dictionary<string, string>();
    param.Add("paramA", paramA);
    param.Add("paramB", paramB);
    
    HttpClient client = new HttpClient();
    HttpFormUrlEncodedContent contents = new HttpFormUrlEncodedContent(param);
    var result = await client.PostAsync(new Uri("http://localhost:8490/api/path/tofunc")
                                                                             , contents);
    var reply = await result.Content.ReadAsStringAsync();
    if (reply.IsSuccessStatusCode)
    { 
    }
    

    希望这会对你有所帮助。

    【讨论】:

      【解决方案3】:
      [HttpPost]
      [Route("api/path/tofunc")]
      public async Task<bool> DoStuffAsync([FromBody]ClientData clientData)
      {
      ....
      }
      

      请在 web api 方法中保留 [FromBody],以便模型绑定器将您的身体数据映射到参数,即 clientData。

      【讨论】:

      • 它没有改变任何东西。正如我所写,我确实得到了 clientData 的值。只有 clientData 中的 paramC 是 ParamsGeneric[0] (或 null,如果我将其更改为单个对象而不是数组)。
      • 为什么要写抽象类(公共抽象类ParamsGeneric)?你能把它变成具体的类并尝试吗?
      • 因为我想传递两种类型的值(ParamsTypeA/ParamsTypeB)。这是继承的优点之一。
      • 这种事情,你必须要覆盖默认的模型绑定器,这个链接会帮助你,*.com/questions/17277578/…