"TryParse"방식으로 json 역 직렬화
내가 소유하지 않은 서비스에 요청을 보낼 때 요청 된 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 절이 있는데, 이는 악취입니다. 음, 악취 이상입니다. 악취가납니다.
표준 실행 경로에서 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;
}
}
혹시라도 캐치 절을 유지했습니다.
함께 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의 패키지를.
업데이트 2 :
댓글에서 언급했듯이 "JSONSchema"에는 가격 모델이 있으므로 무료가 아닙니다 . 여기에서 모든 정보를 찾을 수 있습니다.
@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);
}
어떤 유형에 대해서도 쉽게 사용할 수있는 텍스트로 스키마가 없을 때 사용할 수 있습니다.
@Victor LG's answer using Newtonsoft is close, but it doesn't technically avoid the a catch as the original poster requested. It just moves it elsewhere. Also, though it creates a settings instance to enable catching missing members, those settings aren't passed to the DeserializeObject call so they are actually ignored.
Here's a "catch free" version of his extension method that also includes the missing members flag. The key to avoiding the catch is setting the Error
property of the settings object to a lambda which then sets a flag to indicate failure and clears the error so it doesn't cause an exception.
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;
}
Here's an example to use it:
if(value.TryParseJson(out MyType result))
{
// Do something with result…
}
Just to provide an example of the try/catch approach (it may be useful to somebody).
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;
}
}
Then, it can be used like this:
var result = default(MyObject);
bool isValidObject = jsonString.TryParseJson<MyObject>(out result);
if(isValidObject)
{
// Do something
}
You may deserialize JSON to a dynamic
, and check whether the root element is error
. Note that you probably don't have to check for the presence of status
and code
, like you actually do, unless the server also sends valid non-error responses inside a error
node.
Aside that, I don't think you can do better than a try/catch
.
What actually stinks is that the server sends an HTTP 200 to indicate an error. try/catch
appears simply as checking of inputs.
To test whether a text is valid JSON regardless of schema, you could also do a check on the number of quotation marks:" in your string response, as shown below :
// 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);
ReferenceURL : https://stackoverflow.com/questions/23906220/deserialize-json-in-a-tryparse-way
'programing' 카테고리의 다른 글
Joda 시간에서 시간을 변경하지 않고 시간대를 변환하는 방법 (0) | 2021.01.19 |
---|---|
ASP.NET ID에 클레임을 추가하는 방법 (0) | 2021.01.19 |
AngularJS 템플릿에서 범위 변수가 정의되지 않았는지 확인하는 방법은 무엇입니까? (0) | 2021.01.19 |
이 Crashlytics 컴파일 경고의 원인은 무엇입니까? (0) | 2021.01.19 |
docker-compose 명령으로 COMPOSE_HTTP_TIMEOUT의 기본값을 재정의하는 방법 (0) | 2021.01.19 |