# Import Form Data

> Extract the values of filled PDF forms with Forms.GetAcroFormFields: field types, JSON and C# models, ASP.NET Core uploads and filling a form template again.

- **Product:** TX Text Control .NET Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET Server > PDF Processing > PDF Forms > Import Form Data
- **5255 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/import-data/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/import-data/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/import-data/llms-full.txt

---

Filled PDF forms carry data: the customer's name on an order, the check mark on a consent form, the date of an insurance claim. `Forms.GetAcroFormFields` reads this data from a PDF file or a byte array, without opening the document in an editor. Your code turns the values into JSON or a C# model, accepts forms uploaded to an ASP.NET Core application and transfers the values into a TX Text Control form template.

A first loop over the text fields and check boxes of a PDF form is part of [Read Form Field Values](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/pdf/load-modify/#read-form-field-values) in Getting Started. The classes the method returns are described in [AcroForms](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/acroforms/llms-full.txt). The examples read *Documents/order-form-filled.pdf*, the order form of [Form Fields](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/form-fields/#create-a-fillable-form) after a customer filled it in.

### How GetAcroFormFields Reads a Form

[Forms.GetAcroFormFields](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/forms/getacroformfields-method/llms-full.txt) is a static method with two overloads: one takes the path of a PDF file, the other the PDF as a byte array. It returns an array of [FormField](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formfield/llms-full.txt) objects, each of the class that fits its field, and your code reads the value from the property of that class:

| Class | Value | What to expect |
|---|---|---|
| [FormTextField](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formtextfield/llms-full.txt) | [Value](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formtextfield/value-property/llms-full.txt) | The entered text. A date form field of TX Text Control is a text field in the PDF, so its value is the date as the form displayed it. |
| [FormCheckBox](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formcheckbox/llms-full.txt) | [IsChecked](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formcheckbox/ischecked-property/llms-full.txt) | `true` for a checked box |
| [FormRadioButton](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formradiobutton/llms-full.txt) | `IsChecked` | No member tells which option of a radio group is selected. |
| [FormComboBox](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formcombobox/llms-full.txt), [FormListBox](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formlistbox/llms-full.txt) | [Value](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formchoicefield/value-property/llms-full.txt) | The selected item, or the typed text of an editable combo box. [Options](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formchoicefield/options-property/llms-full.txt) lists the items. |
| [FormButton](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formbutton/llms-full.txt) | None | A push button has no value. |

Plan for these details when you process forms from different sources:

- **Names identify the values.** [FieldName](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formfield/fieldname-property/llms-full.txt) is the name the form's author gave the field. A name can occur more than once in the array, for example for a field that appears in several places, so store values with the indexer of a dictionary instead of `ToDictionary`, which throws for duplicate keys.
- **Empty fields.** `string.IsNullOrWhiteSpace` catches a text field that the user left blank before its value reaches your data model.
- **Derived classes first.** `FormRadioButton` derives from `FormCheckBox` and `FormListBox` from `FormComboBox`, so a `switch` must test the derived class first to tell them apart.
- **Signature fields** aren't part of the array. They are the subject of [Signature Fields](https://docs.textcontrol.com/tx-text-control-dotnet-server/digital-signatures/signature-fields/llms-full.txt).

`GetAcroFormFields` reads forms that users filled in and saved with Adobe Acrobat Reader, with Google Chrome since version 85 or with the Document Viewer.

### Map the Values to JSON and a Model

The following *Program.cs* reads the order form, collects the values by field name, prints them as JSON and fills a C# class with them:

```csharp
using System.Globalization;
using System.Text.Json;
using TXTextControl.DocumentServer.PDF;
using TXTextControl.DocumentServer.PDF.AcroForms;

FormField[] fields = Forms.GetAcroFormFields("Documents/order-form-filled.pdf");

if (fields.Length == 0)
{
    Console.WriteLine("The PDF document has no form fields.");
}

// One entry per name; a name that occurs more than once keeps its last value
Dictionary<string, object?> values = new Dictionary<string, object?>();

foreach (FormField field in fields)
{
    switch (field)
    {
        case FormTextField textField:
            values[field.FieldName] = string.IsNullOrWhiteSpace(textField.Value) ? null : textField.Value;
            break;

        case FormCheckBox checkBox:          // check boxes and radio buttons
            values[field.FieldName] = checkBox.IsChecked;
            break;

        case FormChoiceField choiceField:    // combo boxes and list boxes
            values[field.FieldName] = string.IsNullOrEmpty(choiceField.Value) ? null : choiceField.Value;
            break;
    }
}

Console.WriteLine(JsonSerializer.Serialize(values, new JsonSerializerOptions { WriteIndented = true }));

OrderForm order = new OrderForm
{
    Customer = values.GetValueOrDefault("Customer") as string,
    Product = values.GetValueOrDefault("Product") as string,

    // Parse the date with the culture the form displays it in
    InvoiceDate = DateTime.TryParse(values.GetValueOrDefault("InvoiceDate") as string,
        CultureInfo.GetCultureInfo("en-US"), DateTimeStyles.None, out DateTime date) ? date : null,

    TermsAccepted = values.GetValueOrDefault("TermsAccepted") is true
};

Console.WriteLine($"{order.Customer} ordered {order.Product} on {order.InvoiceDate:yyyy-MM-dd}.");

public class OrderForm
{
    public string? Customer { get; set; }
    public string? Product { get; set; }
    public DateTime? InvoiceDate { get; set; }
    public bool TermsAccepted { get; set; }
}
```

The code works as follows:

- `GetAcroFormFields` reads the file directly. You don't create a `ServerTextControl` for it.
- The `switch` statement uses the base class [FormChoiceField](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/acroforms-formchoicefield/llms-full.txt) for combo boxes and list boxes, and `FormCheckBox` for check boxes and radio buttons, because the program only needs their values.
- Serializing the `FormField` array itself doesn't help: `JsonSerializer` writes only the properties of the base class, without the values. Map each type to its value, as the `switch` statement does.
- The dictionary becomes the JSON object, and the `OrderForm` class gives the rest of your application typed values.

### Accept Uploaded Forms in ASP.NET Core

A web application receives filled forms as uploads. The following controller action reads the uploaded PDF from a byte array and returns its values as JSON:

```csharp
using Microsoft.AspNetCore.Mvc;
using TXTextControl.DocumentServer.PDF;
using TXTextControl.DocumentServer.PDF.AcroForms;

public class FormDataController : Controller
{
    [HttpPost]
    public IActionResult Import(IFormFile document)
    {
        if (document == null || document.Length == 0)
        {
            return BadRequest("Upload a filled PDF form.");
        }

        // Copy the upload into a byte array
        byte[] data;

        using (MemoryStream memoryStream = new MemoryStream())
        {
            document.CopyTo(memoryStream);
            data = memoryStream.ToArray();
        }

        FormField[] fields;

        try
        {
            fields = Forms.GetAcroFormFields(data);
        }
        catch (TXTextControl.FilterException exception)
        {
            return BadRequest($"The PDF form could not be read: {exception.Reason}");
        }

        Dictionary<string, object?> values = new Dictionary<string, object?>();

        foreach (FormField field in fields)
        {
            switch (field)
            {
                case FormTextField textField:
                    values[field.FieldName] = textField.Value;
                    break;

                case FormCheckBox checkBox:
                    values[field.FieldName] = checkBox.IsChecked;
                    break;

                case FormChoiceField choiceField:
                    values[field.FieldName] = choiceField.Value;
                    break;
            }
        }

        return Ok(values);
    }
}
```

The code works as follows:

- The upload is copied into a byte array for the second overload of `GetAcroFormFields`, as [Load Uploaded Documents in ASP.NET Core](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/loading/#load-uploaded-documents-in-asp-net-core) does for other documents.
- A file that can't be read throws a [FilterException](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/filterexception/llms-full.txt). Its [Reason](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/filterexception/reason-property/llms-full.txt) is, for example, `WrongFileType` for a file that isn't a PDF or `InvalidPassword` for a PDF that requires a password to open. `GetAcroFormFields` takes no password, so such forms can't be read.
- The file doesn't import the `TXTextControl` namespace, because both namespaces contain a class named `FormField`. `FilterException` is written in full instead.

### Transfer the Values Into a Form Template

TX Text Control doesn't write values into an existing PDF: the `Forms` class only reads. To create a new PDF with the values, for example a corrected or completed version of the form, transfer them into your template of the form and save it. The following program fills *Documents/order-form.tx* with the values of the filled PDF:

```csharp
using System.Globalization;
using TXTextControl;
using TXTextControl.DocumentServer.PDF;
using AcroForms = TXTextControl.DocumentServer.PDF.AcroForms;

// The fields of the filled PDF by name
Dictionary<string, AcroForms.FormField> pdfFields = new Dictionary<string, AcroForms.FormField>();

foreach (AcroForms.FormField pdfField in Forms.GetAcroFormFields("Documents/order-form-filled.pdf"))
{
    pdfFields[pdfField.FieldName] = pdfField;
}

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();
    tx.Load("Documents/order-form.tx", StreamType.InternalUnicodeFormat);

    // The main text, text frames, headers and footers
    foreach (IFormattedText textPart in tx.TextParts)
    {
        foreach (FormField formField in textPart.FormFields)
        {
            if (string.IsNullOrEmpty(formField.Name) ||
                !pdfFields.TryGetValue(formField.Name, out AcroForms.FormField? pdfField))
            {
                continue;
            }

            switch (formField)
            {
                case TextFormField textFormField when pdfField is AcroForms.FormTextField textField:
                    if (!string.IsNullOrWhiteSpace(textField.Value))
                    {
                        textFormField.Text = textField.Value;
                    }
                    break;

                case CheckFormField checkFormField when pdfField is AcroForms.FormCheckBox checkBox:
                    checkFormField.Checked = checkBox.IsChecked;
                    break;

                case SelectionFormField selectionFormField when pdfField is AcroForms.FormChoiceField choiceField:
                    // -1 selects nothing if the value isn't one of the items
                    selectionFormField.SelectedIndex = Array.IndexOf(selectionFormField.Items, choiceField.Value);
                    break;

                case DateFormField dateFormField when pdfField is AcroForms.FormTextField dateText:
                    // In the PDF, a date form field is a text field
                    dateFormField.Date = DateTime.TryParse(dateText.Value, CultureInfo.GetCultureInfo("en-US"),
                        DateTimeStyles.None, out DateTime date) ? date : null;
                    break;
            }
        }
    }

    tx.Save("Documents/order-form-imported.pdf", StreamType.AdobePDF);
}
```

The code works as follows:

- The alias `AcroForms` separates the classes of the PDF from the form fields of the document, which live in the `TXTextControl` namespace.
- [ServerTextControl.TextParts](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/servertextcontrol/textparts-property/llms-full.txt) covers the form fields in headers, footers and text frames, too.
- Each form field type receives its value through its own property: [Text](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/textfield/text-property/llms-full.txt) for text boxes, [Checked](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/checkformfield/checked-property/llms-full.txt) for check boxes, [SelectedIndex](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/selectionformfield/selectedindex-property/llms-full.txt) for selection fields and [Date](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/dateformfield/date-property/llms-full.txt) for date fields.
- Saved with `StreamType.AdobePDF`, the template becomes a fillable PDF with the imported values. To archive the values as fixed text, flatten the fields before saving, as [Flatten Forms](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/flatten/llms-full.txt) describes.

Instead of setting each field, you can also merge the values with `MailMerge` and `FormFieldMergeType.Preselect`, as [Merge Form Fields](https://docs.textcontrol.com/tx-text-control-dotnet-server/mail-merge/fields/#merge-form-fields) shows. The blog article [Create, Pre-Select, Flatten and Extract PDF Form Fields using C#](https://www.textcontrol.com/blog/2023/08/14/create-preselect-flatten-and-extract-pdf-form-fields-using-csharp/) combines creating, filling and reading a form in one example.

### Next Steps

- [Export XFDF](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/export-xfdf/llms-full.txt): store the imported values as XFDF.
- [Flatten Forms](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/flatten/llms-full.txt): turn the filled form into a document with fixed values.
- [AcroForms](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/acroforms/llms-full.txt): the classes of PDF form fields and how forms are created.
- [Form Fields](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-viewer/form-fields/llms-full.txt) in Document Viewer: let users fill out forms in your application.

---

## Nearby

- **Previous:** [AcroForms](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/acroforms/llms-full.txt)
- **Next:** [Export XFDF](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/export-xfdf/llms-full.txt)
