Konvertere HAL+JSON

Et eksempel på hvordan man kan lage en tilpasset HAL+JSON konverterer for å serialisere og deserialisere lister av rettigheter med System.Text.Json.

Tilpasset HAL+JSON konverterer

(eng: custom HAL+JSON converter)

I denne leksjonen vil vi vise hvordan man lager en tilpasset konverterer for HAL+JSON for System.Text.Json. Detaljene vil være sterkt knyttet til System.Text.Json og vil ikke kunne brukes uten modifikasjoner mot andre JSON biblioteker. Vi har basert implementasjonen på Microsoft - How to write custom converters for JSON serialization (marshalling) in .NET og bruker factory pattern.

JsonConverterFactory

public class EmbeddedListConverter : JsonConverterFactory

Tilpassede konverterere som bruker factory mønsteret må implementere to funksjoner:

  • CanConvert() som bestemmer om konvertereren støtter denne typen,
  • CreateConverter() som lager en JsonConverter som kan håndtere serialisering og deserialisering av denne typen.
public override bool CanConvert(Type typeToConvert)
{
    if (!typeToConvert.IsGenericType)
    {
        return false;
    }

    if (typeToConvert.GetGenericTypeDefinition() != typeof(List<>))
    {
        return false; 
    }

    Type listType = typeToConvert.GetGenericArguments()[0];
    if (listType != typeof(Right))
    {
        return false;
    }

    return true; 
}

Denne implementasjonen av CanConvert() vil kun akseptere List<Right>. Den eneste endringen som må gjøres for å håndtere lister av andre objekter, som for eksempel Roles, er å legge til en sjekk som aksepterer den her. Resten av konvertereren trenger ingen endring for å støtte det.

public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
    Type listValueType = typeToConvert.GetGenericArguments()[0];

    JsonConverter converter = (JsonConverter)Activator.CreateInstance(
        typeof(EmbeddedListConverterInner<>).MakeGenericType(
            new Type[] { listValueType }),
        BindingFlags.Instance | BindingFlags.Public,
        binder: null,
        args: new object[] { options },
        culture: null);

    return converter;
}

JsonConverter

Oppgaven som skal utføres av JsonConverter er å konvertere mellom lister av objekter i klientkoden og JSON som aksepteres av Altinn. I dette eksempelet vil vi benytte modellen for en rettighet Right. System.Text.Json støtter allerede serialisering av objekter hvor alle attributtene har enkle typer (se: JsonSerilizerOptions.GetConverter()). Det som gjenstår å beskrive er hvordan lister av disse objektene skal serialiseres (Write()) og deserialiseres (Read()).

Eksempel på HAL+JSON

{
    "_links": {
        ...
    },
    "_embedded": {
        "rights": [
            {
                "RightID": 0,
                "RightType": "Service",
                "ServiceCode": "1337",
                "ServiceEditionCode": 1,
                "Action": "Read",
                "RightSourceType": "DirectlyDelegatedRights",
                "IsDelegatable": true
            },
            {
                "RightID": 1,
                ...
            }
        ]
    }
}

EmbeddedListConverterInner klassen

private class EmbeddedListConverterInner<T> : JsonConverter<List<T>>
{
    private readonly JsonConverter<T> _converter;
    private readonly Type _type;

    public EmbeddedListConverterInner(JsonSerializerOptions options)
    {
        _converter = (JsonConverter<T>)options.GetConverter(typeof(T));
        _type = typeof(T);
    }
    

Read

Read()

  • lage listen med Rights,
  • kalle konvertereren for Rights som ble satt i konstruktøren,
  • ignorere og gå videre når den kommer til JsonTokenType.EndObject,
  • returnere listen når den kommer til slutten av array.
public override List<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
    var list = new List<T>();

    while (reader.Read())
    {
        if (reader.TokenType == JsonTokenType.EndObject)
        {
            continue;
        }
        if (reader.TokenType == JsonTokenType.EndArray)
        {
            return list;
        }

        T element = _converter.Read(ref reader, _type, options);
        list.Add(element);
    }
}

Write

Write() skal kun legge til array notasjon rundt utlistingen av Rights.

public override void Write(Utf8JsonWriter writer, List<T> list, JsonSerializerOptions options)
{
    writer.WriteStartArray();
    foreach (T value in list)
    {
        _converter.Write(writer, value, options);
    }
    writer.WriteEndArray();
}

I neste leksjon skal vi bruke HAL+JSON modellene og konvertereren til å håndtere responsen fra GET {who}/authorization/Delegations.

Se også