JSON
Serialization
TryParse
Deserialization
Programming

Deserialize json in a TryParse way

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Many JSON APIs sit on unreliable boundaries: user input, files, queues, or third-party services. In those places, a TryParse-style API is often more convenient than letting deserialization throw exceptions into normal control flow.

What a TryParse-style JSON API should do

A useful TryParse helper should separate three outcomes clearly:

  • the payload was not valid JSON
  • the JSON was valid but did not map to the expected type
  • the JSON mapped structurally, but business validation still failed

In C#, System.Text.Json throws exceptions for invalid input. Wrapping that behind a TryDeserialize method gives the caller a familiar boolean pattern.

csharp
1using System;
2using System.Text.Json;
3
4public static class JsonHelper
5{
6    private static readonly JsonSerializerOptions Options = new()
7    {
8        PropertyNameCaseInsensitive = true
9    };
10
11    public static bool TryDeserialize<T>(
12        string json,
13        out T? value,
14        out string? error)
15    {
16        value = default;
17        error = null;
18
19        if (string.IsNullOrWhiteSpace(json))
20        {
21            error = "Input was empty.";
22            return false;
23        }
24
25        try
26        {
27            value = JsonSerializer.Deserialize<T>(json, Options);
28            if (value is null)
29            {
30                error = "JSON was valid but produced null.";
31                return false;
32            }
33
34            return true;
35        }
36        catch (JsonException ex)
37        {
38            error = $"Invalid JSON: {ex.Message}";
39            return false;
40        }
41        catch (NotSupportedException ex)
42        {
43            error = $"Unsupported target type: {ex.Message}";
44            return false;
45        }
46    }
47}

This keeps the boundary explicit and easy to test.

Using the helper in real code

The caller can branch cleanly without a large try block:

csharp
1public sealed record OrderRequest(string OrderId, decimal Total);
2
3string payload = """
4{
5  "orderId": "A-1001",
6  "total": 129.95
7}
8""";
9
10if (JsonHelper.TryDeserialize<OrderRequest>(payload, out var order, out var error))
11{
12    Console.WriteLine($"Order {order!.OrderId} accepted.");
13}
14else
15{
16    Console.WriteLine($"Rejected payload: {error}");
17}

That reads more naturally in ingestion code, background jobs, and defensive API boundaries where invalid data is expected occasionally.

Parsing is not the same as validation

A successful deserialization only means the JSON was syntactically valid and could be mapped into the requested type. It does not prove the data is acceptable for your business rules.

For example:

csharp
1public static bool TryValidate(OrderRequest request, out string? error)
2{
3    error = null;
4
5    if (string.IsNullOrWhiteSpace(request.OrderId))
6    {
7        error = "OrderId is required.";
8        return false;
9    }
10
11    if (request.Total <= 0)
12    {
13        error = "Total must be greater than zero.";
14        return false;
15    }
16
17    return true;
18}

The usual flow is:

  1. Try deserialize
  2. Validate business rules
  3. Process the object

Keeping those stages separate makes error reporting much easier to reason about.

When not to use this pattern

A TryParse-style API is best at the edges of the system. Inside trusted internal code, exceptions may still be appropriate, especially when invalid JSON means something is seriously wrong and should fail fast.

You should also think about whether a boolean plus error string is enough. For larger codebases, a richer result type can be more expressive, but the TryParse pattern remains a good mental model.

Common Pitfalls

One common mistake is catching every exception type and hiding important failure details. Limit the catch blocks to JSON-related failures and surface useful messages safely.

Another issue is returning success when the deserializer yields null. Unless your API explicitly allows that, treat it as failure so the caller does not trip over a null reference later.

People also blur structural parsing and business validation. A payload can be perfectly valid JSON and still contain an impossible amount, a missing identifier, or an invalid state transition.

Finally, avoid duplicating serializer options in many places. If one entry point uses case-insensitive parsing and another does not, debugging becomes unnecessarily painful.

Summary

  • A TryParse-style wrapper makes JSON parsing safer and more readable at system boundaries.
  • 'System.Text.Json can be wrapped to return success, value, and error text instead of throwing into normal flow.'
  • Deserialization success does not replace domain validation.
  • Treat null output deliberately rather than assuming success.
  • Centralize serializer options so parsing behavior stays consistent.

Course illustration
Course illustration

All Rights Reserved.