如何确定JSON响应是否将绑定到模型

时间:2017-12-09 06:49:42

标签: c# json deserialization

我们有第三方服务提供商,根据操作,使用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等,则转到下一个似乎是错误的。

3 个答案:

答案 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);

反射需要更长时间,但稍后在添加新类时更容易扩展