programing

"TryParse" 방식으로 json 직렬화 해제

minimums 2023. 2. 27. 23:04
반응형

"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;
}

여기, 표준 실행 경로에 있을 수 있는 빈 캐치 조항이 있습니다. 나쁜 냄새입니다.음, 나쁜 냄새 이상이야. 악취가 나.

표준 실행 경로에서 잡히지 않도록 응답을 "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;
    }
}

난 캐치 조항을 지켰어...혹시 모르니까

@빅터LG의 뉴턴소프트 사용 답변은 비슷하지만 엄밀히 따지면 포스터의 요구대로 잡히지 않는다.그냥 다른 곳으로 옮겨요.또한 누락된 멤버를 잡을 수 있도록 설정 인스턴스를 생성하지만 이러한 설정은 DiserializeObject 호출에 전달되지 않으므로 실제로 무시됩니다.

부재 플래그가 포함된 확장 방식의 "캐치 프리" 버전이 여기에 있습니다.잡히지 않게 하는 열쇠는, 「유닛」의 설정해 두는 것입니다.Error설정 객체의 속성을 람다로 설정합니다. 그러면 플래그가 실패를 나타내도록 설정하고 오류를 지워서 예외를 발생시키지 않습니다.

 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…
}

를 사용하여 스키마에 대해 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);

업데이트:

스키마 검증은 더 이상 메인 뉴턴소프트의 일부가 아닙니다.Json 패키지, 뉴턴소프트를 추가해야 할 거야.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);
}

이것은 스키마가 없는 경우 어떤 유형에서도 즉시 사용할 수 있는 텍스트로 사용할 수 있습니다.

트라이/캐치 접근법의 예를 제시합니다(누군가에게 유용할 수 있습니다).

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
}

에의 JSON시리얼화를 해제해, 루트 요소가 다음과 같은지를 확인할 수 있습니다.errorstatus ★★★★★★★★★★★★★★★★★」code와 같이,가 「」에서도 을 송신하지 한, 「」errordiscloss.discloss.

는 더 잘 할 수 것 try/catch.

실제로 문제가 되는 것은 서버가 오류를 나타내기 위해 HTTP 200을 보낸다는 것입니다. try/catch는 단순히 입력 확인으로 나타납니다.

클래스에 Error 속성을 추가하거나 다음과 같이 이 오류 속성과 함께 기본 클래스를 사용하는 것이 좋습니다.

public class BaseResult
{
    public Error Error { get; set; }
    public bool HasError => String.IsNullOrEmpty(Error?.Code);
}

public class Error
{
    public string Status { get; set; }
    public string Code { get; set; }
}

결과 클래스는 이 기본 결과에서 상속됩니다.

public class MyOkResponseClass : BaseResult
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public int Prop3 { get; set; }
}

그런 다음 HasError 속성을 확인할 수 있습니다.예외도 없고, 확장 방식도 없고, 이상한 체크도 없어요.

스키마에 관계없이 텍스트가 유효한 JSON인지 여부를 테스트하려면 다음과 같이 문자열 응답에서 따옴표:"를 확인할 수도 있습니다.

// Invalid JSON
var responseContent = "asgdg"; 
// var responseContent = "{ \"ip\" = \"11.161.195.10\" }";

// 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\"";
// var responseContent = "{}";

int count = 0;
foreach (char c in responseContent)
    if (c == '\"') count++; // Escape character needed to display quotation
if (count >= 2 || responseContent == "{}") 
{
    // Valid Json
    try {
        JToken parsedJson = JToken.Parse(responseContent);
        Console.WriteLine("RESPONSE: Json- " + parsedJson.ToString(Formatting.Indented));
    }  
    catch(Exception ex){
        Console.WriteLine("RESPONSE: InvalidJson- " + responseContent);
    }
}
else
    Console.WriteLine("RESPONSE: InvalidJson- " + responseContent);

언급URL : https://stackoverflow.com/questions/23906220/deserialize-json-in-a-tryparse-way

반응형