Reading JSON with custom formats in C#
Tags: csharp, dotnet, tutorial, beginners
Published: 2022-10-14 04:51:47

## The Problem
The JSON is the most common type used by APIs to respond requests. 
Sometimes we receive contents that aren't possible to parse using built-in types in C#. Example, imagine that an API responds a date like Brazilian format (dd/MM/yyy) and we need to read this value in our app. 

For this example, lets assume the JSON below:
```json
{"name":"John","birthDate":"31/12/1990"}
```

### Preparing the scenario
First of all, we need to create the class that represents the person.

```csharp
public class Person
{
    [JsonPropertyName("name")]
    public string Name { get; set; }

    [JsonPropertyName("birthDate")]
    public DateTime BirthDate { get; set; }

    public override string ToString()
    {
        return $"Name: {Name} | BirthDate: {BirthDate}";
    }
}
```

Now we need to create a variable that represents the JSON.

```csharp
public class Program
{
    public static string jsonExample = "{"
        + "\"name\":\"John\","
        + "\"birthDate\":\"31/12/1990\""
        + "}";
    
    public static void Main(string[] args)
    {
    }
}
```

And finally we create the parser using the built-in `JsonSerializer.Deserialize`

```csharp
public class Program
{
    public static string jsonExample = "{"
        + "\"name\":\"John\","
        + "\"birthDate\":\"31/12/1990\""
        + "}";
    public static void Main(string[] args)
    {
        
        var person = JsonSerializer.Deserialize<Person>(jsonExample);
        Console.WriteLine(person);
    }
}
```

### Running the code

When we try to run the code we receive an exception informing `System.FormatException : The JSON value is not in a supported DateTime format.` because the C# don't know how to apply the custom date to the type `DateTime`.

## The solution

To solve this problem, we need to create a custom json converter using the built-in abstract class `JsonConverter<T>`.

First we need to create a class that inherit from `JsonConverter<T>` where `T` is the type of expected result. In this case `DateTime`.

```csharp
public class MyCustomJsonConverter : JsonConverter<DateTime>
{
    
}
```

After that we implements the inherited methods.
```csharp
public class MyCustomJsonConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}
```

Now we need to implement two methods, the first one is to read a custom json value and the second one is to write a custom json value. For this purpose we will use only the first one, because we are reading a value.

The system will pass the expected json using the `Utf8JsonReader reader` parameter. 
As Utf8JsonReader is a struct, it's necessary to get the value using the `GetString()` that returns a `string?` type.

And finally we use the `DateTime.ParseExact()` to format the date as expected.

```csharp
public class MyCustomJsonConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var dateTimeFromJson = reader.GetString()!;

        return DateTime.ParseExact(dateTimeFromJson, "dd/MM/yyyy", CultureInfo.InvariantCulture);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}
```

Ok, we have a custom converter, now we need to apply this converter on property `Person.BirthDate` to the serializer do the hard work for us.

It's simple. We just need to decorate the `BirthDate` property with the attribute `JsonConverter()` that expects a type of converter.

```csharp
public class Person
{
    [JsonPropertyName("name")]
    public string Name { get; set; }

    [JsonPropertyName("birthDate")]
    [JsonConverter(typeof(MyCustomJsonConverter))]
    public DateTime BirthDate { get; set; }

    public override string ToString()
    {
        return $"Name: {Name} | BirthDate: {BirthDate}";
    }
}
```

### Running the code

It's all we need to create the custom converter. Now we can just run the code and see the result.

`Name: John | BirthDate: 1990-12-31 12:00:00 AM`


#### Source 
[How to write custom converters for JSON serialization (marshalling) in .NET](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?pivots=dotnet-6-0)

#### Credits
Photo by [James Harrison](https://unsplash.com/@jstrippa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/code?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)