当我向服务发送请求(我不拥有)时,它可能会响应所请求的JSON数据,或者出现如下错误:
{
"error": {
"status": "error message",
"code": "999"
}
}
在这两种情况下,HTTP响应代码都是200 OK,所以我不能用它来确定是否有错误 - 我必须反序列化要检查的响应。 所以我有一些看起来像这样的东西:
bool TryParseResponseToError(string jsonResponse, out Error error)
{
// Check expected error keywords presence
// before try clause to avoid catch performance drawbacks
if (jsonResponse.Contains("error") &&
jsonResponse.Contains("status") &&
jsonResponse.Contains("code"))
{
try
{
error = new JsonSerializer<Error>().DeserializeFromString(jsonResponse);
return true;
}
catch
{
// The JSON response seemed to be an error, but failed to deserialize.
// Or, it may be a successful JSON response: do nothing.
}
}
error = null;
return false;
}
在这里,我有一个空的catch子句,可能在标准执行路径中,这是一种难闻的气味......好吧,不仅仅是一种难闻的气味:它很臭。
您是否知道更好的“TryParse”响应方式,以便避免标准执行路径中的捕获?
感谢Yuval Itzchakov的答案,我改进了我的方法:
bool TryParseResponse(string jsonResponse, out Error error)
{
// Check expected error keywords presence :
if (!jsonResponse.Contains("error") ||
!jsonResponse.Contains("status") ||
!jsonResponse.Contains("code"))
{
error = null;
return false;
}
// Check json schema :
const string errorJsonSchema =
@"{
'type': 'object',
'properties': {
'error': {'type':'object'},
'status': {'type': 'string'},
'code': {'type': 'string'}
},
'additionalProperties': false
}";
JsonSchema schema = JsonSchema.Parse(errorJsonSchema);
JObject jsonObject = JObject.Parse(jsonResponse);
if (!jsonObject.IsValid(schema))
{
error = null;
return false;
}
// Try to deserialize :
try
{
error = new JsonSerializer<Error>.DeserializeFromString(jsonResponse);
return true;
}
catch
{
// The JSON response seemed to be an error, but failed to deserialize.
// This case should not occur...
error = null;
return false;
}
}
我保留了捕获条款......以防万一。
答案 0 :(得分:46)
使用Json.NET
,您可以针对架构验证您的json:
string schemaJson = @"{
'status': {'type': 'string'},
'error': {'type': 'string'},
'code': {'type': 'string'}
}";
JsonSchema schema = JsonSchema.Parse(schemaJson);
JObject jobj = JObject.Parse(yourJsonHere);
if (jobj.IsValid(schema))
{
// Do stuff
}
然后在TryParse方法中使用它。
public static T TryParseJson<T>(this string json, string schema) where T : new()
{
JsonSchema parsedSchema = JsonSchema.Parse(schema);
JObject jObject = JObject.Parse(json);
return jObject.IsValid(parsedSchema) ?
JsonConvert.DeserializeObject<T>(json) : default(T);
}
然后做:
var myType = myJsonString.TryParseJson<AwsomeType>(schema);
请注意,架构验证不再是主要的Newtonsoft.Json软件包的一部分,您需要添加Newtonsoft.Json.Schema软件包。
如评论中所述,&#34; JSONSchema&#34;有一个定价模型,意思是它不是免费的。您可以找到所有信息here
答案 1 :(得分:21)
@ Yuval答案的略微修改版本。
static T TryParse<T>(string jsonData) where T : new()
{
JSchemaGenerator generator = new JSchemaGenerator();
JSchema parsedSchema = generator.Generate(typeof(T));
JObject jObject = JObject.Parse(jsonData);
return jObject.IsValid(parsedSchema) ?
JsonConvert.DeserializeObject<T>(jsonData) : default(T);
}
如果您没有随时可用于任何类型的文本架构,则可以使用此选项。
答案 2 :(得分:17)
@Victor LG使用Newtonsoft的答案很接近,但是从技术上讲,它并不能避免原始海报要求的问题。它只是将其移至其他位置。同样,尽管它创建了一个设置实例来捕获丢失的成员,但是这些设置不会传递给DeserializeObject调用,因此实际上将被忽略。
这是他的扩展方法的“免费版本”,其中还包括缺少成员标志。避免捕获的关键是将设置对象的Error
属性设置为lambda,然后该lambda设置一个标志以指示失败并清除错误,从而不会引起异常。
public static bool TryParseJson<T>(this string @this, out T result)
{
bool success = true;
var settings = new JsonSerializerSettings
{
Error = (sender, args) => { success = false; args.ErrorContext.Handled = true; },
MissingMemberHandling = MissingMemberHandling.Error
};
result = JsonConvert.DeserializeObject<T>(@this, settings);
return success;
}
以下是使用它的示例:
if(value.TryParseJson(out MyType result))
{
// Do something with result…
}
答案 3 :(得分:10)
仅提供try / catch方法的示例(对某些人可能有用)。
public static bool TryParseJson<T>(this string obj, out T result)
{
try
{
// Validate missing fields of object
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;
result = JsonConvert.DeserializeObject<T>(obj, settings);
return true;
}
catch (Exception)
{
result = default(T);
return false;
}
}
然后,它可以像这样使用:
var result = default(MyObject);
bool isValidObject = jsonString.TryParseJson<MyObject>(out result);
if(isValidObject)
{
// Do something
}
答案 4 :(得分:3)
您可以deserialize JSON to a dynamic
,并检查根元素是否为error
。请注意,您可能不必像实际那样检查是否存在status
和code
,除非服务器还在error
内发送有效的非错误响应节点
除此之外,我认为你做得比try/catch
更好。
实际发生的问题是服务器发送HTTP 200来指示错误。 try/catch
只是检查输入。
答案 5 :(得分:-1)
要测试文本与模式无关是否为有效的JSON,您还可以检查字符串响应中引号的数量:“,如下所示:
// Invalid JSON
var responseContent = "asgdg";
// Valid JSON, uncomment to test these
// var responseContent = "{ \"ip\": \"11.161.195.10\", \"city\": \"York\", \"region\": \"Ontartio\", \"country\": \"IN\", \"loc\": \"-43.7334,79.3329\", \"postal\": \"M1C\", \"org\": \"AS577 Bell Afgh\", \"readme\": \"https://ipinfo.io/missingauth\"}";
// var responseContent = "\"asfasf\"";
int count = 0;
foreach (char c in responseContent)
if (c == '\"') count++; // Escape character needed to display quotation
if (count >= 2)
{
// Valid Json
JToken parsedJson = JToken.Parse(responseContent);
Console.WriteLine("RESPONSE: Json- " + parsedJson.ToString(Formatting.Indented));
}
else
Console.WriteLine("RESPONSE: InvalidJson- " + responseContent);