我们有第三方服务提供商,根据操作,使用JSON将Webhook发送到我们的webhook网址。由于我只能提供1个webhook网址,所有通知都需要汇集到1个网址。我需要确定要反序列化的模型。
我理解如何将JSON字符串反序列化为对象模型。
以下是首次发送到我们的webhook的示例
{
"providerAccount": {
"id": XXXXXX,
"aggregationSource": "USER",
"refreshInfo": {
"statusCode": 801,
"statusMessage": "ADD_IN_PROGRESS",
"status": "IN_PROGRESS",
"additionalStatus": "LOGIN_IN_PROGRESS"
}
}
}
然后又过了10秒左右,另一个JSON webhook进来了。
{
"event": {
"info": "REFRESH.INTERIM_PROGRESS",
"loginName": "XXXXXX",
"data": {
"providerAccount": {
"id": XXXXXX,
"providerId": XXXXXX,
"isManual": false,
"createdDate": "2017-12-08",
"aggregationSource": "USER",
"refreshInfo": {
"status": "IN_PROGRESS",
"additionalStatus": "LOGIN_SUCCESS",
"statusCode": 0,
"statusMessage": "OK",
"nextRefreshScheduled": "0001-01-01T00:00:00",
"lastRefreshed": "0001-01-01T00:00:00",
"lastRefreshAttempt": "0001-01-01T00:00:00"
},
"loginForm": null
}
}
}
}
正如您所看到的,它们将是我需要反序列化的两个独立的模型对象。
我发现在第一个模型上检查第二个模型的null似乎是错误的,可以用不同的方式实现。
还有另外3个不同的JSON字符串来自同一个webhook。并检查模型是否为null,如果此模型为null等,则转到下一个似乎是错误的。
答案 0 :(得分:2)
我建议使用Newtonsoft.json软件包,如果您还没有使用它,可以通过nuget安装。您可以轻松地接受所有JSON请求作为JContainer,然后检查字段,如果该字段存在,您可以将JSON反序列化为适当的模型。
如果你有4个不同的模型,那么你可能希望你有4个具有不同结构的半独特JSON字符串,可以通过检查特定字段,对象或数组来识别。
public HttpResponseMessage WebhookCallback(JContainer jsonData)
{
// Check to make sure we're getting an object not an array.
if(jsonData.Type == JTokenType.Object)
{
var jObject = jsonData as JObject;
// If this is an event type object parse it depending on the value of the info field.
if(jObject["event"] != null)
{
if (jObject["info"] != null && jObject["info"].ToString() == "REFRESH.INTERIM_PROGRESS")
{
InterimProgress interimProgress = Newtonsoft.Json.JsonConvert.DeserializeObject<InterimProgress>(jObject["data"].ToString());
}
if (jObject["info"] != null && jObject["info"].ToString() == "REFRESH.END_PROGRESS")
{
EndProgress interimProgress = Newtonsoft.Json.JsonConvert.DeserializeObject<EndProgress>(jObject["data"].ToString());
}
}
else if (jObject["providerAccount"] != null)
{
ProviderAccount providerAccount = jObject.ToObject<ProviderAccount>();
}
}
return Request.CreateResponse(HttpStatusCode.OK);
}
如果您不想首先反序列化为JContainer,您可以始终尝试匹配唯一键,然后反序列化为正确的对象。
答案 1 :(得分:1)
您可以使用json架构来确保传入的json确实与您的某个模型完全匹配。可以从模型生成模式(有关详细信息,请参阅here)。示例代码(安装Newtonsoft.Json.Schemas
包):
public class ModelMapper {
private readonly List<SchemaAndHandler> _handlers = new List<SchemaAndHandler>();
public void AddModelHandler<T>(Func<T, object> handler) {
// generate schema once
var schema = new JSchemaGenerator().Generate(typeof(T));
_handlers.Add(new SchemaAndHandler(schema, typeof(T), o => handler((T) o)));
}
public object Parse(string json) {
var raw = JObject.Parse(json);
foreach (var handler in _handlers) {
// validate according to each schema
if (raw.IsValid(handler.Schema)) {
return handler.Handler(JsonConvert.DeserializeObject(json, handler.Type));
}
}
// or throw
return null;
}
private class SchemaAndHandler {
public SchemaAndHandler(JSchema schema, Type type, Func<object, object> handler) {
Schema = schema;
Type = type;
Handler = handler;
}
public JSchema Schema { get; }
public Type Type { get; }
public Func<object, object> Handler { get; }
}
}
public class Model1 {
public int Id { get; set; }
}
public class Model2 {
public string Name { get; set; }
}
用法:
// should be reused - no need to create per every request
var mapper = new ModelMapper();
// this also needs to be done only once
mapper.AddModelHandler((Model1 model) => {
// do something
return model;
});
mapper.AddModelHandler((Model2 model) => {
// do something
return model;
});
// parse incoming json to some result
var result = mapper.Parse("{Name: \"test\"}");
如果您不需要对已解析的模型执行任何特定操作,只需将其传递到某处,则可以简化:
public class ModelMapper {
private readonly List<Tuple<Type, JSchema>> _schemas = new List<Tuple<Type, JSchema>>();
public ModelMapper(params Type[] models) {
foreach (var model in models) {
var schema = new JSchemaGenerator().Generate(model);
_schemas.Add(Tuple.Create(model, schema));
}
}
public object ParseModel(string json) {
var raw = JObject.Parse(json);
foreach (var schema in _schemas) {
// validate according to each schema
if (raw.IsValid(schema.Item2)) {
return JsonConvert.DeserializeObject(json, schema.Item1);
}
}
// or throw
return null;
}
}
然后用法:
// should be reused - no need to create per every request
var mapper = new ModelMapper(typeof(Model1), typeof(Model2));
var result = mapper.ParseModel("{Name: \"test\"}");
答案 2 :(得分:0)
如果所有Json对象都有不同的对象名,您可以使用它来提取该名称并获取c#对象类型,并反序列化它。
要获取对象名称,您可以使用Newtonsoft.Json包并将其解析为JObject,或者您可以编写正则表达式来提取它,以防解析对您来说太昂贵。 JObject示例:
private string GetJsonObjectName(string json)
{
var jobj = JObject.Parse(json);
return ((JProperty)jobj.First).Name;
}
获得名称后,您可以添加简单的开关并根据字符串获取类型,或者将具有Json对象名称的属性添加到类中并使用反射。
public class JsonType : Attribute
{
private string _name;
public JsonType(string name)
{
_name = name;
}
override
public string ToString()
{
return _name;
}
}
[JsonType("providerAccount")]
public class RootObjectProvider
{
public ProviderAccount providerAccount { get; set; }
}
使用反射来获得正确的对象:
var type = Assembly.GetExecutingAssembly().GetTypes()
.Where(x => Attribute.IsDefined(x,typeof(JsonType)))
.SingleOrDefault(x => x.GetCustomAttribute(typeof(JsonType)).ToString() == jsonObjectName);
var cobj = JsonConvert.DeserializeObject(json1, type);
反射需要更长时间,但稍后在添加新类时更容易扩展