Wenn ich eine Anfrage an einen Dienst sende (den ich nicht besitze), antwortet dieser möglicherweise entweder mit den angeforderten JSON-Daten oder mit einem Fehler, der wie folgt aussieht:
{
"error": {
"status": "error message",
"code": "999"
}
}
In beiden Fällen ist der HTTP-Antwortcode 200 OK, daher kann ich damit nicht feststellen, ob ein Fehler vorliegt oder nicht. Ich muss die Antwort deserialisieren, um sie zu überprüfen. Ich habe also etwas, das so aussieht:
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;
}
Hier habe ich eine leere catch-Klausel, die sich möglicherweise im Standardausführungspfad befindet, was ein schlechter Geruch ist ... Nun, mehr als ein schlechter Geruch: Es stinkt.
Kennen Sie einen besseren Weg, um die Antwort "TryParse" zu machen, um einen Haken im Standardausführungspfad zu vermeiden ?
[BEARBEITEN]
Dank der Antwort von Yuval Itzchakov habe ich meine Methode folgendermaßen verbessert:
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;
}
}
Ich habe die catch-Klausel beibehalten ... nur für den Fall.
Die Antwort von @Victor LG mit Newtonsoft ist nah, aber es vermeidet technisch nicht den Haken, den das Originalplakat angefordert hat. Es bewegt es einfach woanders hin. Obwohl eine Einstellungsinstanz erstellt wird, mit der fehlende Mitglieder abgefangen werden können, werden diese Einstellungen nicht an den DeserializeObject-Aufruf übergeben, sodass sie tatsächlich ignoriert werden.
Hier ist eine "catch free" -Version seiner Erweiterungsmethode, die auch das Flag für fehlende Mitglieder enthält. Der Schlüssel zum Vermeiden des Fangs besteht darin, die
Error
Eigenschaft des Einstellungsobjekts auf ein Lambda zu setzen, das dann ein Flag setzt, um einen Fehler anzuzeigen, und den Fehler löscht, damit keine Ausnahme verursacht wird.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; }
Hier ist ein Beispiel, um es zu verwenden:
if(value.TryParseJson(out MyType result)) { // Do something with result… }
quelle
JsonConvert.DeserializeObject<T>("{}")
wird keine Ausnahme ausgelöst,T
es sei denn, es handelt sich um einen Array-Typ (oder eine Eigenschaft ist erforderlich, wie Sie sagten).Eine leicht modifizierte Version von @ Yuvals Antwort.
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); }
Dies kann verwendet werden, wenn das Schema als Text für keinen Typ verfügbar ist.
quelle
JObject jObject = JObject.Parse(jsonData);
Prüfung ist für unseren Fall überflüssig.Nur um ein Beispiel für den Try / Catch-Ansatz zu geben (er kann für jemanden nützlich sein).
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; } }
Dann kann es so verwendet werden:
var result = default(MyObject); bool isValidObject = jsonString.TryParseJson<MyObject>(out result); if(isValidObject) { // Do something }
quelle
result = JsonConvert.DeserializeObject<T>(obj, settings);
-catch (JsonSerializationException ex)
,catch (Exception)
sodass false zurückgegeben wird, wenn ungültiger Json an ihn übergeben wird.Sie können JSON zu a deserialisieren
dynamic
und prüfen, ob das Stammelement vorhanden isterror
. Beachten Sie, dass Sie wahrscheinlich nicht auf das Vorhandensein vonstatus
und überprüfen müssencode
, wie Sie es tatsächlich tun, es sei denn, der Server sendet auch gültige fehlerfreie Antworten innerhalb eineserror
Knotens.Abgesehen davon glaube ich nicht, dass Sie es besser machen können als a
try/catch
.Was tatsächlich stinkt, ist, dass der Server ein HTTP 200 sendet, um einen Fehler anzuzeigen.
try/catch
erscheint einfach als Überprüfung der Eingänge.quelle
Um zu testen, ob ein Text unabhängig vom Schema ein gültiger JSON-Code ist, können Sie auch die Anzahl der Anführungszeichen überprüfen: "in Ihrer Zeichenfolgenantwort, wie unten gezeigt:
// 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);
quelle