Ignore Deserialization Errors in System.Text.Json
Learn to handle malformed JSON in System.Text.Json JsonSerializer.Deserialize by ignoring errors like unclosed braces or bad quotes. Use custom JsonConverters, JsonSerializerOptions, and preprocessing for tolerant deserialization like Newtonsoft.Json.
How to ignore deserialization errors in System.Text.Json’s JsonSerializer.Deserialize for malformed or broken JSON?
I need to partially deserialize broken JSON data, bypassing errors such as unclosed curly braces or improper quote escaping (e.g., using ‘’ instead of ").
In Newtonsoft.Json, the Error event in JsonSerializerSettings allowed configuring an error handler to continue deserialization. What is the equivalent approach in System.Text.Json?
Tools like Visual Studio and VS Code can parse such files despite errors—how can I achieve similar tolerant deserialization?
System.Text.Json’s JsonSerializer.Deserialize throws a JsonException on malformed JSON like unclosed curly braces or unescaped single quotes, with no built-in equivalent to Newtonsoft.Json’s Error event for tolerant deserialization. You can achieve partial parsing using custom JsonConverters to skip bad properties or values, JsonSerializerOptions for minor fixes like trailing commas, or preprocess the string to repair common issues. This mimics VS Code’s forgiving display by focusing on salvageable parts rather than failing entirely.
Contents
- Understanding Deserialization Errors in System.Text.Json
- Why No Direct Equivalent to Newtonsoft.Json’s Error Event?
- Built-in JsonSerializerOptions for Partial Tolerance
- Custom JsonConverter for Ignoring Property-Level Errors
- Workarounds: Pre-Processing Malformed JSON
- Comparing System.Text.Json vs. Newtonsoft.Json
- Best Practices and Future Outlook
- Sources
- Conclusion
Understanding Deserialization Errors in System.Text.Json
Ever loaded a JSON file riddled with typos—maybe a missing closing brace or someone using single quotes instead of properly escaped doubles—and watched your app crash? That’s the reality with System.Text.Json’s JsonSerializer.Deserialize. It demands strict RFC 8259 compliance, hurling a JsonException at the first whiff of trouble.
Take this broken snippet:
{
"name": "Test",
"age": "thirty", // Not a number!
"items": [1, "two", { "id": 3
}
Deserializing to a class like:
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public List<object> Items { get; set; }
}
Yields something like: System.Text.Json.JsonException: The JSON value could not be converted to System.Int32. Path: $.age | LineNumber: 3 | BytePositionInLine: 10.
The error helpfully includes Path, LineNumber, and BytePositionInLine—great for debugging, lousy for production data from unreliable sources. Unlike VS Code, which renders partial trees with squiggles, JsonSerializer.Deserialize bails out completely. No good properties get populated. Frustrating when you’re dealing with log files, user uploads, or legacy exports.
And those single quotes? "description": 'it's broken' triggers Unexpected character encountered while parsing value. System.Text.Json won’t auto-escape or ignore them.
Why No Direct Equivalent to Newtonsoft.Json’s Error Event?
Newtonsoft.Json spoiled us with that Error event in JsonSerializerSettings. Hook it up, log the issue, set e.ErrorContext.Handled = true, and deserialization chugs on, skipping the bad bit.
var settings = new JsonSerializerSettings {
Error = (sender, e) => {
Console.WriteLine($"Error at {e.ErrorContext.Path}: {e.ErrorContext.Error.Message}");
e.ErrorContext.Handled = true;
}
};
var obj = JsonConvert.DeserializeObject<Person>(json, settings); // Partial success!
System.Text.Json? Zilch. No event, no callback. The official migration guide from Microsoft lists missing features bluntly: no Error handler. GitHub tracks requests like #44390 and #51238, proposing options like IgnoreReadErrors. As of .NET 8 (and into 2026), it’s still “Future.”
Why? Performance and security. System.Text.Json is a lean UTF-8 parser optimized for speed—tolerant mode could open injection doors or slow things down. But for real-world mess? You improvise.
Built-in JsonSerializerOptions for Partial Tolerance
Don’t overlook the low-hanging fruit. JsonSerializerOptions handles some non-standard JSON, edging toward VS Code tolerance.
Key tweaks:
AllowTrailingCommas: true– Eats extra commas after arrays/objects.ReadCommentHandling: JsonCommentHandling.Skip– Ignores/* comments */or// lines.NumberHandling = JsonNumberHandling.AllowReadingFromString– Parses"123"as int.
var options = new JsonSerializerOptions {
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
NumberHandling = JsonNumberHandling.AllowReadingFromString
};
var obj = JsonSerializer.Deserialize<Person>(json, options);
This fixes trailing commas or string numbers but chokes on unclosed braces or single quotes. For dates, add PropertyNameCaseInsensitive = true or custom formatters, but syntax errors? Nope.
From ASP.NET Core issues, even these don’t cover malformed escapes. It’s a start, not salvation.
Custom JsonConverter for Ignoring Property-Level Errors
Here’s the powerhouse: Write a JsonConverter<T> that catches exceptions per property or value, returning defaults. Makolyte’s SafeIntConverter nails it for numbers.
public class SafeIntConverter : JsonConverter<int> {
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
try {
return reader.GetInt32();
} catch {
return 0; // Or default(int), null for Nullable<int>
}
}
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) {
writer.WriteNumberValue(value);
}
}
Register it:
var options = new JsonSerializerOptions();
options.Converters.Add(new SafeIntConverter());
var person = JsonSerializer.Deserialize<Person>(json, options);
// Age becomes 0 instead of failing!
Scale up for objects/arrays. Stack Overflow’s array skipper does this:
public class TolerantListConverter<T> : JsonConverter<List<T>> {
public override List<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
var list = new List<T>();
var doc = JsonDocument.ParseValue(ref reader);
foreach (var element in doc.RootElement.EnumerateArray()) {
try {
list.Add(element.Deserialize<T>(options));
} catch { /* Skip bad item */ }
}
return list;
}
// Write omitted...
}
Apply to properties: [JsonConverter(typeof(TolerantListConverter<object>))]. Boom—partial deserialization, skipping junk like VS Code’s tree.
Performance hit? Minimal for small objects. Test it.
Workarounds: Pre-Processing Malformed JSON
When converters fall short (e.g., structural breaks), fix the JSON first. Regex? Risky, but quick for quotes/braces.
string FixQuotes(string json) => Regex.Replace(json, @"'([^']*)'", "\"$1\""); // Naive single -> double
string FixBraces(string json) {
int open = json.Count(c => c == '{');
int close = json.Count(c => c == '}');
return json + new string('}', open - close);
}
Then deserialize. Better: Use JsonDocument for DOM parsing, extract good nodes.
using var doc = JsonDocument.Parse(json, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip });
if (doc.RootElement.TryGetProperty("name", out var nameProp)) {
person.Name = nameProp.GetString();
}
// Manually populate, ignoring errors.
Fallback to Newtonsoft for tough cases—hybrid approach. Or libraries like Json.More for extras.
Pros: Full control. Cons: Custom code explodes for complex schemas.
Comparing System.Text.Json vs. Newtonsoft.Json
| Feature | System.Text.Json | Newtonsoft.Json |
|---|---|---|
| Error Handling | Custom converters/options only | Error event—set Handled=true |
| Speed | 2-3x faster | Slower but flexible |
| Tolerance | Strict syntax, partial via hacks | Highly forgiving |
| Memory | Low (no allocations) | Higher |
| Size | Built-in, tiny | NuGet package |
Code side-by-side:
- Newtonsoft: One-liner with settings.
- System.Text.Json: Converter boilerplate.
Stick with Newtonsoft for legacy tolerance, migrate for perf. Newtonsoft docs show why it’s gold standard—until GitHub delivers.
Best Practices and Future Outlook
Log errors always—wrap Deserialize in try-catch, capture exception.Path. Validate upstream if possible. Unit test with broken JSON.
For .NET 9+, watch #51238—error callbacks might land. Meanwhile, custom converters cover 80% cases. Secure your fixes; malformed JSON can be attack vectors.
Profile: Converters add ~10-20% overhead on bad data. Good enough?
Sources
- Migrate from Newtonsoft.Json to System.Text.Json — Official Microsoft guide on missing features like Error event: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft
- C#: How to Ignore JSON Deserialization Errors — Custom SafeIntConverter and GitHub issue details: https://makolyte.com/csharp-how-to-ignore-json-deserialization-errors/
- How do I ignore exceptions during deserialization of bad JSON? — Stack Overflow solution for skipping bad array items: https://stackoverflow.com/questions/60012370/how-do-i-ignore-exceptions-during-deserialization-of-bad-json
- System.Text.Json error handling equivalency to Newtonsoft.Json — Explains JsonException details and lack of handler: https://stackoverflow.com/questions/66177062/system-text-json-error-handling-equivalency-to-newtonsoft-json-json-net-jsonse
- Add API to ignore errors during deserialization — GitHub runtime issue proposing error handler: https://github.com/dotnet/runtime/issues/44390
- Proposal: Add ability to ignore parsing errors — GitHub discussion on JsonSerializerOptions for tolerance: https://github.com/dotnet/runtime/issues/51238
- Json.NET Error Handling — Newtonsoft documentation on Error event usage: https://www.newtonsoft.com/json/help/html/serializationerrorhandling.htm
Conclusion
No silver bullet matches Newtonsoft’s Error event in System.Text.Json’s JsonSerializer.Deserialize, but custom JsonConverters and JsonSerializerOptions deliver robust partial deserialization for malformed JSON—handling unclosed braces, bad quotes, and more. Start with converters for property-level skips; preprocess for structure fixes. Keep an eye on .NET updates, test thoroughly, and you’ll get that VS Code-style forgiveness without switching libraries. Your apps will thank you.