Skip to content

DictionaryTKeyEnumTValueConverter does not play nicely with Xamarin iOS/Android and AOT in general #16922

@softlion

Description

@softlion

AOT does not like reflection and factories building objects from reflection, as it needs to detect all types used in the app from the static analysis, to be able to strip unused classes and methods, and to precompile used classes and methods to machine code for performance.

The example you provide DictionaryTKeyEnumTValueConverter fall typically in this case.

The way to go is to use the inner class as the converter. So make i made it public, without requiring the json options, as it is unavailable there.

public class DictionaryEnumConverter<TKey, TValue> : JsonConverter<Dictionary<TKey, TValue>> where TKey : struct, Enum
    {
        private JsonConverter<TValue> _valueConverter;
        private bool valueConverterSearched = false;
        private Type _valueType = typeof(TValue);

        public DictionaryEnumConverter()
        {
        }

        public override Dictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            // For performance, use the existing converter if available.
            if (!valueConverterSearched)
            {
                _valueConverter = (JsonConverter<TValue>)options.GetConverter(typeof(TValue));
                valueConverterSearched = true;
            }

            if (reader.TokenType != JsonTokenType.StartObject)
                throw new JsonException();

            var dictionary = new Dictionary<TKey, TValue>();

            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndObject)
                    return dictionary;

                // Get the key.
                if (reader.TokenType != JsonTokenType.PropertyName)
                    throw new JsonException();

                var propertyName = reader.GetString();

                // For performance, parse with ignoreCase:false first.
                if (!Enum.TryParse(propertyName, ignoreCase: false, out TKey key) &&
                    !Enum.TryParse(propertyName, ignoreCase: true, out key))
                {
                    throw new JsonException($"Unable to convert \"{propertyName}\" to Enum \"{typeof(TKey)}\".");
                }

                // Get the value.
                TValue v;
                if (_valueConverter != null)
                {
                    reader.Read();
                    v = _valueConverter.Read(ref reader, _valueType, options);
                }
                else
                {
                    v = JsonSerializer.Deserialize<TValue>(ref reader, options);
                }

                // Add to dictionary.
                dictionary.Add(key, v);
            }

            throw new JsonException("Missing EndObject on dictionary");
        }

        public override void Write(Utf8JsonWriter writer, Dictionary<TKey, TValue> dictionary, JsonSerializerOptions options)
        {
            // For performance, use the existing converter if available.
            if (!valueConverterSearched)
            {
                _valueConverter = (JsonConverter<TValue>)options.GetConverter(typeof(TValue));
                valueConverterSearched = true;
            }

            writer.WriteStartObject();

            foreach (var kvp in dictionary)
            {
                writer.WritePropertyName(kvp.Key.ToString());

                if (_valueConverter != null)
                    _valueConverter.Write(writer, kvp.Value, options);
                else
                    JsonSerializer.Serialize(writer, kvp.Value, options);
            }

            writer.WriteEndObject();
        }
    }

Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Metadata

Metadata

Assignees

Labels

dotnet/svcneeds-more-infoNeeds more info from OP. Auto-closed after 2 weeks if no response. [org][resolution]

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions