| File: FormMapping\Converters\DictionaryConverter.cs | Web Access |
| Project: src\aspnetcore\src\Components\Endpoints\src\Microsoft.AspNetCore.Components.Endpoints.csproj (Microsoft.AspNetCore.Components.Endpoints) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; namespace Microsoft.AspNetCore.Components.Endpoints.FormMapping; internal abstract class DictionaryConverter<TDictionary> : FormDataConverter<TDictionary> { } internal sealed class DictionaryConverter<TDictionary, TDictionaryPolicy, TBuffer, TKey, TValue> : DictionaryConverter<TDictionary> where TKey : ISpanParsable<TKey> where TDictionaryPolicy : IDictionaryBufferAdapter<TDictionary, TBuffer, TKey, TValue> { private readonly FormDataConverter<TValue> _valueConverter; private static readonly Type _elementType = typeof(TValue); public DictionaryConverter(FormDataConverter<TValue> elementConverter) { ArgumentNullException.ThrowIfNull(elementConverter); _valueConverter = elementConverter; } [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)] [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)] internal override bool TryRead( ref FormDataReader context, Type type, FormDataMapperOptions options, [NotNullWhen(true)] out TDictionary? result, out bool found) { TValue currentValue; TBuffer buffer; bool foundCurrentValue; bool currentElementSuccess; bool succeeded = true; var keys = context.GetKeys(); found = keys.HasValues(); if (!found) { result = default!; return true; } buffer = TDictionaryPolicy.CreateBuffer(); // We can't precompute dictionary anyKeys ahead of time, // so the moment we find a dictionary, we request the list of anyKeys // for the current location, which will involve parsing the form data anyKeys // and building a tree of anyKeys. var keyCount = 0; var maxCollectionSize = options.MaxCollectionSize; foreach (var key in keys) { context.PushPrefix(key.Span); currentElementSuccess = _valueConverter.TryRead(ref context, _elementType, options, out currentValue!, out foundCurrentValue); succeeded &= currentElementSuccess; context.PopPrefix(key.Span); if (!TKey.TryParse(key[1..^1].Span, CultureInfo.InvariantCulture, out var keyValue)) { succeeded = false; var currentPrefix = context.GetPrefix(); context.AddMappingError( FormattableStringFactory.Create(FormDataResources.DictionaryUnparsableKey, key[1..^1], currentPrefix), null); continue; } keyCount++; if (keyCount > maxCollectionSize) { context.AddMappingError( FormattableStringFactory.Create(FormDataResources.MaxCollectionSizeReached, "dictionary", maxCollectionSize), null); succeeded = false; break; } TDictionaryPolicy.Add(ref buffer, keyValue!, currentValue); } result = TDictionaryPolicy.ToResult(buffer); if (!succeeded) { context.AttachInstanceToErrors(result!); } return succeeded; } }