You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求推荐C#轻量库:将HTTP POST Multipart数据解析为对象/数组树

Parsing Multipart POST Data into a Nested Tree Structure in C#

Absolutely! There are a couple of great options in C# for parsing multipart POST data and converting that flat form field structure into a nested tree of objects, arrays, and primitive values—just like how PHP handles $_POST automatically. Let’s break down your best choices:


1. ASP.NET Core: Built-in Support (No Extra Libraries Needed)

If you’re working in an ASP.NET Core project, the framework’s native model binding already handles PHP-style form naming out of the box.

First, define a model that matches your expected nested structure:

public class SubmittedForm
{
    public bool Boolean { get; set; }
    public int Integer { get; set; }
    public string String { get; set; }
    public List<object> Array { get; set; }
    public NestedObject Object { get; set; }
}

public class NestedObject
{
    public bool Boolean { get; set; }
    public int Integer { get; set; }
    public string String { get; set; }
}

Then, in your controller action, just accept the model directly—ASP.NET Core will automatically unflatten the form data for you:

[HttpPost]
public IActionResult ProcessForm(SubmittedForm formData)
{
    // formData is already your nested tree structure!
    return Ok(formData);
}

If you need to manually parse multipart data (e.g., alongside file uploads), use the MultipartReader from Microsoft.AspNetCore.WebUtilities:

using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;

var boundary = MediaTypeHeaderValue.Parse(Request.ContentType).Boundary.Value;
var reader = new MultipartReader(boundary, Request.Body);
var section = await reader.ReadNextSectionAsync();

var flatFormData = new Dictionary<string, string>();
while (section != null)
{
    var contentDisposition = ContentDispositionHeaderValue.Parse(section.ContentDisposition);
    if (!contentDisposition.FileName.HasValue)
    {
        // This is a form field, not a file
        var fieldName = contentDisposition.Name.Trim('"');
        var fieldValue = await section.ReadAsStringAsync();
        flatFormData.Add(fieldName, fieldValue);
    }
    section = await reader.ReadNextSectionAsync();
}

// Use the unflattening logic below to convert flatFormData to nested structure

2. General .NET Projects: HttpMultipartParser

For non-ASP.NET Core projects (like console apps or desktop apps), the lightweight HttpMultipartParser library makes parsing multipart data straightforward.

First, install it via NuGet:

Install-Package HttpMultipartParser

Then parse the data and unflatten it:

using HttpMultipartParser;

// Assume you have a Stream containing the multipart request data
var parser = await MultipartFormDataParser.ParseAsync(requestStream);
var flatFormData = parser.Parameters.ToDictionary(p => p.Name, p => p.Data);

// Convert the flat data to your nested tree structure
var nestedData = UnflattenFormData(flatFormData);

3. DIY Unflattening Logic (If You Prefer No Libraries)

If you want full control, here’s a custom implementation that handles both array[] (auto-appended arrays) and object[key] (nested objects) syntax, matching PHP’s behavior:

public static object UnflattenFormData(Dictionary<string, string> flatData)
{
    var result = new Dictionary<string, object>();

    foreach (var kvp in flatData)
    {
        var keyParts = ParseFormKey(kvp.Key);
        var currentContainer = result as IDictionary<string, object>;
        object parentContainer = null;

        for (int i = 0; i < keyParts.Count; i++)
        {
            var part = keyParts[i];
            if (part.IsArray)
            {
                // Handle array fields (array[] or array[0])
                if (!currentContainer.ContainsKey(part.BaseName) || !(currentContainer[part.BaseName] is List<object> array))
                {
                    array = new List<object>();
                    currentContainer[part.BaseName] = array;
                }
                else
                {
                    array = currentContainer[part.BaseName] as List<object>;
                }

                if (part.Index.HasValue)
                {
                    // Ensure array has enough elements for the specified index
                    while (array.Count <= part.Index.Value)
                        array.Add(null);
                    array[part.Index.Value] = ParseValue(kvp.Value);
                }
                else
                {
                    // Append to array (for array[] syntax)
                    array.Add(ParseValue(kvp.Value));
                }

                parentContainer = array;
                currentContainer = null; // Arrays can't be nested object containers
            }
            else
            {
                // Handle nested object fields (object[key])
                if (!currentContainer.ContainsKey(part.BaseName) || !(currentContainer[part.BaseName] is IDictionary<string, object> nestedObj))
                {
                    nestedObj = new Dictionary<string, object>();
                    currentContainer[part.BaseName] = nestedObj;
                }
                else
                {
                    nestedObj = currentContainer[part.BaseName] as IDictionary<string, object>;
                }

                if (i == keyParts.Count - 1)
                {
                    // Final part: set the value
                    nestedObj[part.BaseName] = ParseValue(kvp.Value);
                }
                else
                {
                    // Move to the nested object container
                    parentContainer = currentContainer;
                    currentContainer = nestedObj;
                }
            }
        }
    }

    return result;
}

// Helper to parse form keys like "array[]" or "object[boolean]"
private static List<KeyPart> ParseFormKey(string key)
{
    var parts = new List<KeyPart>();
    var currentName = new StringBuilder();
    int i = 0;

    while (i < key.Length)
    {
        if (key[i] == '[')
        {
            if (currentName.Length > 0)
            {
                parts.Add(new KeyPart { BaseName = currentName.ToString() });
                currentName.Clear();
            }
            i++;
            var insideBrackets = new StringBuilder();
            while (i < key.Length && key[i] != ']')
            {
                insideBrackets.Append(key[i]);
                i++;
            }
            if (insideBrackets.Length == 0)
            {
                // Mark as array (array[])
                parts.Last().IsArray = true;
            }
            else if (int.TryParse(insideBrackets.ToString(), out int idx))
            {
                // Mark as array with specific index (array[2])
                parts.Last().IsArray = true;
                parts.Last().Index = idx;
            }
            else
            {
                // Nested object key (object[key])
                parts.Add(new KeyPart { BaseName = insideBrackets.ToString() });
            }
            i++; // Skip closing ]
        }
        else
        {
            currentName.Append(key[i]);
            i++;
        }
    }

    if (currentName.Length > 0)
    {
        parts.Add(new KeyPart { BaseName = currentName.ToString() });
    }

    return parts;
}

// Helper to convert string values to appropriate types (bool, int, etc.)
private static object ParseValue(string value)
{
    if (bool.TryParse(value, out bool boolVal)) return boolVal;
    if (int.TryParse(value, out int intVal)) return intVal;
    if (double.TryParse(value, out double doubleVal)) return doubleVal;
    return value;
}

// Helper class for parsed key parts
private class KeyPart
{
    public string BaseName { get; set; }
    public bool IsArray { get; set; }
    public int? Index { get; set; }
}

内容的提问来源于stack exchange,提问作者Virus721

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:08:38