0

Use case: I want to write a JsonConverter for a generic type like this, but I cannot apply it to the type itself:

public class EditingModelConverter<T> : JsonConverter<EditingModel<T>>
{
    // ... 
}

[JsonConverter(typeof(EditingModelConverter<T>))] // Error
public class EditingModel<T>(FrozenDictionary<string, object> values)
{
    // ...
}

The error message is a CS0416:

'EditingModelConverter': an attribute argument cannot use type parameters

My questions is it possible to do this without writing a converter factory? I think a factory would be the most proper solution if there is no workaround?

6
  • @Sweeper thanks, I forgot to mention that. Added the System.Text.Json tag. Commented Oct 27 at 9:25
  • 1
    C# generics are only know at compile time when they are used with value types, otherwise they are a runtime construct. I'd have to try this out to, but it may be possible to limit your generic type parameter to where T : struct to be able to use the parameter as a type parameter to a attribute. blog.stephencleary.com/2022/10/… Commented Oct 27 at 9:27
  • @JonSkeet I disagree with the choice of the duplicate target. The duplicate target is about why generic subclasses of System.Attribute is not allowed. The attribute in this question is JsonConverterAttribute, which is not generic. The error is simply because the type parameter T is out of scope at that position. That said, I believe the question should stay closed until it is edited to ask only one question. Commented Oct 27 at 9:37
  • @Sweeper: Ah, you're right, sorry. Reopened as "open" is better than "closed with bad dupe" IMO. But yes, it would be better to have a single question. Commented Oct 27 at 10:22
  • Thank you, I edited it to keep one question only Commented Oct 27 at 10:24

2 Answers 2

3

Not sure if there is a workaround or not, but writing a converterfactory is not that difficult. You could try something like this:

public class EditingModelConverterFactory : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert)
    {
        if (!typeToConvert.IsGenericType)
        {
            return false;
        }

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

        return true;
    }

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

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

        return converter;
    }
}

assuming your EditingModelConverter has a public constructor with a JsonSerializerOptions as a parameter.

Sign up to request clarification or add additional context in comments.

Comments

1

My questions is it possible to do this without writing a converter factory?

JsonConverterAttribute has an CreateConverter method that can be used to extend custom json converter mapping, here's an example:

public class GenericJsonConverterAttribute(Type genericTypeDefinition)
    : JsonConverterAttribute() // Dont pass the type arg
{
    private Type ConverterTypeDefinition => genericTypeDefinition;

    // If typeToConvert is Something<A>
    // It instantiates EditingModelConverter<A>
    public override JsonConverter? CreateConverter(Type typeToConvert)
        => Activator.CreateInstance(ConverterTypeDefinition
          .MakeGenericType(typeToConvert.GenericTypeArguments)) as JsonConverter;
}

[GenericJsonConverter(typeof(EditingModelConverter<>))]
public class EditingModel<T>(Dictionary<string, object> values)
{
    // ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.