Extraction schema:
{"$defs": {"Box12Entry": {"properties": {"amount": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Dollar amount printed beside the Box 12 code in any printed slot: '12a See instructions for box 12', '12b', '12c', or '12d' on the IRS blank, or in the register column '12 See Instrs. for Box 12'. JSON number exactly as printed; null if a code prints with no amount.", "title": "Box 12 amount"}, "code": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "The letter code of a Box 12 entry (values like C, D, DD, W), verbatim uppercase as printed in the vertical 'Code' rail of the blank form or at the left edge of the register column '12 See Instrs. for Box 12'. Identifies the row — Box 12 rows are aligned by this code at eval time, not by position.", "title": "Box 12 code"}}, "title": "Box 12 entry", "type": "object"}, "Box14Entry": {"properties": {"amount": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Dollar amount printed beside the description in box '14 Other'. JSON number exactly as printed; null if a description prints with no amount.", "title": "Box 14 amount"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Text label of a box '14 Other' entry, verbatim as printed, e.g. RISDI on this wave. Identifies the row — Box 14 rows are aligned by this description at eval time, not by position.", "title": "Box 14 description"}}, "title": "Box 14 entry", "type": "object"}, "StateLocalRow1": {"properties": {"employer_state_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box 15 'Employer’s state ID number' (the register variant caption reads 'state ID No.'). The state identification number verbatim as printed, keeping leading zeros, e.g. 05051707900. (row 1 of the two printed 'boxes 15-20' entry rows).", "title": "15 Employer’s state ID number"}, "local_income_tax": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Amount in box '19 Local income tax' - the LOCAL tax column, distinct from box '17 State income tax' (do not copy the state amount here). Null when box 19 is blank, as it is on all RI register docs.", "title": "19 Local income tax"}, "local_wages": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Amount in box '18 Local wages, tips, etc.' - the LOCAL wages column, distinct from box '16 State wages, tips, etc.' (do not copy the state amount here). Null when box 18 is blank, as it is on all RI register docs.", "title": "18 Local wages, tips, etc."}, "locality_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box '20 Locality name' for the single printed state row, verbatim as printed; null if the box is blank (blank on all wave docs). (row 1 of the two printed 'boxes 15-20' entry rows).", "title": "20 Locality name"}, "state": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box '15 State' — the two-letter state abbreviation of the single printed state row, verbatim, e.g. RI. This copy prints one state row only; a multi-state W-2 (a second row in boxes 15-20) is out of scope for this schema version. (row 1 of the two printed 'boxes 15-20' entry rows).", "title": "15 State"}, "state_income_tax": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '17 State income tax' for the single printed state row. Dollar amount as a JSON number, exactly as printed; null if the box is blank. (row 1 of the two printed 'boxes 15-20' entry rows).", "title": "17 State income tax"}, "state_wages": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '16 State wages, tips, etc.' for the single printed state row. Dollar amount as a JSON number, exactly as printed; null if the box is blank. (row 1 of the two printed 'boxes 15-20' entry rows).", "title": "16 State wages, tips, etc."}}, "type": "object"}, "StateLocalRow2": {"properties": {"employer_state_id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box 15 'Employer’s state ID number' (the register variant caption reads 'state ID No.'). The state identification number verbatim as printed, keeping leading zeros, e.g. 05051707900. (row 2 of the two printed 'boxes 15-20' entry rows). The employer-reference register variant prints only one row - null there.", "title": "15 Employer’s state ID number"}, "local_income_tax": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Amount in box '19 Local income tax' - the LOCAL tax column, distinct from box '17 State income tax' (do not copy the state amount here). Null when box 19 is blank, as it is on all RI register docs. (row 2 of the two printed 'boxes 15-20' entry rows).", "title": "19 Local income tax"}, "local_wages": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Amount in box '18 Local wages, tips, etc.' - the LOCAL wages column, distinct from box '16 State wages, tips, etc.' (do not copy the state amount here). Null when box 18 is blank, as it is on all RI register docs. (row 2 of the two printed 'boxes 15-20' entry rows).", "title": "18 Local wages, tips, etc."}, "locality_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box '20 Locality name' for the single printed state row, verbatim as printed; null if the box is blank (blank on all wave docs). (row 2 of the two printed 'boxes 15-20' entry rows). The employer-reference register variant prints only one row - null there.", "title": "20 Locality name"}, "state": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box '15 State' — the two-letter state abbreviation of the single printed state row, verbatim, e.g. RI. This copy prints one state row only; a multi-state W-2 (a second row in boxes 15-20) is out of scope for this schema version. (row 2 of the two printed 'boxes 15-20' entry rows). The employer-reference register variant prints only one row - null there.", "title": "15 State"}, "state_income_tax": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '17 State income tax' for the single printed state row. Dollar amount as a JSON number, exactly as printed; null if the box is blank. (row 2 of the two printed 'boxes 15-20' entry rows). The employer-reference register variant prints only one row - null there.", "title": "17 State income tax"}, "state_wages": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '16 State wages, tips, etc.' for the single printed state row. Dollar amount as a JSON number, exactly as printed; null if the box is blank. (row 2 of the two printed 'boxes 15-20' entry rows). The employer-reference register variant prints only one row - null there.", "title": "16 State wages, tips, etc."}}, "type": "object"}}, "properties": {"box_10_dependent_care_benefits": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '10 Dependent care benefits'. Dollar amount as a JSON number, exactly as printed; null if the box is blank (printed but typically empty on this wave).", "title": "10 Dependent care benefits"}, "box_11_nonqualified_plans": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '11 Nonqualified plans'. Dollar amount as a JSON number, exactly as printed; null if the box is blank (printed but typically empty on this wave).", "title": "11 Nonqualified plans"}, "box_12": {"anyOf": [{"items": {"$ref": "#/$defs/Box12Entry"}, "type": "array"}, {"type": "null"}], "default": null, "description": "Box 12 coded amounts. The blank form prints four slots '12a See instructions for box 12', '12b', '12c', '12d', each with a vertical 'Code' rail; the register variant prints a single column headed '12 See Instrs. for Box 12' with one code letter and amount per line. One row per printed code entry, in print order; null if this copy prints no Box 12 entries.", "title": "12 Box 12 coded amounts"}, "box_13_retirement_plan": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Box 13 'Retirement plan' checkbox — second cell of the box 13 checkbox grid. True if marked (X); false if the Box 13 grid is printed but this box is unmarked; null only if this copy omits the Box 13 grid.", "title": "13 Retirement plan"}, "box_13_statutory_employee": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Box 13 'Statutory employee' checkbox — first cell of the box 13 checkbox grid (the register variant prints the grid compactly under the label '13' with a fourth Subtotal cell). True if marked (X); false if the Box 13 grid is printed but this box is unmarked; null only if this copy omits the Box 13 grid.", "title": "13 Statutory employee"}, "box_13_subtotal": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "'Subtotal' checkbox — the fourth cell of the compact box 13 grid printed only on the employer reference register variant (an employer bookkeeping mark for subtotal rows, not a field of the IRS blank form). True if marked (X); false if the register grid prints the Subtotal checkbox but it is unmarked; null if this copy has no Subtotal checkbox, e.g. the IRS blank form. The register grid ALWAYS prints this checkbox: false when unmarked - null is wrong on these docs.", "title": "13 Subtotal (register variant only)"}, "box_13_third_party_sick_pay": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Box 13 'Third-party sick pay' checkbox — third cell of the box 13 checkbox grid. True if marked (X); false if the Box 13 grid is printed but this box is unmarked; null only if this copy omits the Box 13 grid.", "title": "13 Third-party sick pay"}, "box_14_other": {"anyOf": [{"items": {"$ref": "#/$defs/Box14Entry"}, "type": "array"}, {"type": "null"}], "default": null, "description": "Box '14 Other' — employer-defined deductions and benefits, e.g. RISDI on this wave. One row per printed description and amount pair, in print order; null if box 14 is blank.", "title": "14 Other"}, "box_1_wages": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '1 Wages, tips, other compensation'. Dollar amount as a JSON number, exactly as printed (this register prints plain decimals such as 40093.72 — no thousands separators or dollar signs); null if the box is blank.", "title": "1 Wages, tips, other compensation"}, "box_2_federal_income_tax_withheld": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '2 Federal income tax withheld'. Dollar amount as a JSON number, exactly as printed; null if the box is blank.", "title": "2 Federal income tax withheld"}, "box_3_social_security_wages": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '3 Social security wages'. Dollar amount as a JSON number, exactly as printed; null if the box is blank.", "title": "3 Social security wages"}, "box_4_social_security_tax_withheld": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '4 Social security tax withheld'. Dollar amount as a JSON number, exactly as printed; null if the box is blank.", "title": "4 Social security tax withheld"}, "box_5_medicare_wages_and_tips": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '5 Medicare wages and tips'. Dollar amount as a JSON number, exactly as printed; null if the box is blank.", "title": "5 Medicare wages and tips"}, "box_6_medicare_tax_withheld": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '6 Medicare tax withheld'. Dollar amount as a JSON number, exactly as printed; null if the box is blank.", "title": "6 Medicare tax withheld"}, "box_7_social_security_tips": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '7 Social security tips'. Dollar amount as a JSON number, exactly as printed; null if the box is blank (printed but typically empty on this wave).", "title": "7 Social security tips"}, "box_8_allocated_tips": {"anyOf": [{"type": "number"}, {"type": "null"}], "default": null, "description": "Box '8 Allocated tips'. Dollar amount as a JSON number, exactly as printed; null if the box is blank (printed but typically empty on this wave).", "title": "8 Allocated tips"}, "control_number_line1": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box 'd Control number' - the FIRST printed line only (e.g. '0012-0012N426'). Verbatim.", "title": "Control Number Line 1"}, "control_number_line2": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box 'd Control number' - the SECOND printed line only (e.g. '0000000165-ELECTR'), the register sequence/type line. Verbatim; null if the box prints a single line.", "title": "Control Number Line 2"}, "employee_address": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Employee address: box 'f Employee’s address and ZIP code' on the blank form; the register variant prints it as the lines after the name inside the combined block 'e Employee’s name, address, and ZIP code'. Join printed lines with comma + space — street, then city, state and ZIP, verbatim, e.g. 221 EMERSON RD, HARRISVILLE RI 02830.", "title": "f Employee address and ZIP code"}, "employee_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Employee full name as printed. Blank form boxes: 'e Employee’s first name and initial', 'Last name', 'Suff.'; the register variant prints the name as the first line of the combined block 'e Employee’s name, address, and ZIP code'. Keep middle initial and any suffix verbatim, e.g. JENNA M ANTHONY.", "title": "e Employee name (first name and initial, last name, suffix)"}, "employee_ssn": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box 'a Employee's social security number'. These copies print a masked value, often with overstruck characters. Policy: if the last four digits are legibly printed (e.g. 'XXX-XX-9186'), return 'XXX-XX-' plus those digits; if the tail is fully masked/overstruck, return exactly 'XXX-XX-XXXX' (canonical 4-X form) - do NOT try to count the struck-out X characters. Null only if box a is entirely absent.", "title": "a Employee’s social security number"}, "employer_address": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "The lines of box 'c Employer’s name, address, and ZIP code' after the employer name — street or PO box, then city, state and ZIP, verbatim. Join printed lines with comma + space, e.g. PO BOX 107, PASCOAG RI 02859.", "title": "c Employer address and ZIP code"}, "employer_ein": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Box 'b Employer identification number (EIN)'; the register variant caption under letter b reads 'identification number'. The employer federal EIN verbatim as printed, keeping the hyphen, e.g. 05-0517079.", "title": "b Employer identification number (EIN)"}, "employer_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "First line of box 'c Employer’s name, address, and ZIP code' — the employer name only, verbatim as printed, e.g. PASCOAG UTILITY DISTRICT.", "title": "c Employer name"}, "state_local_row_1": {"anyOf": [{"$ref": "#/$defs/StateLocalRow1"}, {"type": "null"}], "default": null, "description": "The first printed entry row of boxes '15 State' through '20 Locality name'. Null when this row is entirely blank or not printed.", "title": "State/Local Row 1"}, "state_local_row_2": {"anyOf": [{"$ref": "#/$defs/StateLocalRow2"}, {"type": "null"}], "default": null, "description": "The second printed entry row of boxes '15 State' through '20 Locality name'. Null when this row is entirely blank or not printed.", "title": "State/Local Row 2"}, "void": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "The 'Void' checkbox printed beside box d. True ONLY if a mark (X or similar) is printed INSIDE the Void box; false when the box is printed but empty - the box outline alone is not a mark. Null only if no Void box is printed on this copy.", "title": "Void checkbox"}}, "title": "Form W-2 Wage and Tax Statement (2020)", "type": "object"}

Expected output:
{"box_10_dependent_care_benefits": null, "box_11_nonqualified_plans": null, "box_12": null, "box_13_retirement_plan": true, "box_13_statutory_employee": false, "box_13_subtotal": false, "box_13_third_party_sick_pay": false, "box_14_other": [{"description": "RISDI", "amount": 17.24}], "box_1_wages": 1325, "box_2_federal_income_tax_withheld": 63.65, "box_3_social_security_wages": 1325, "box_4_social_security_tax_withheld": 82.15, "box_5_medicare_wages_and_tips": 1325, "box_6_medicare_tax_withheld": 19.21, "box_7_social_security_tips": null, "box_8_allocated_tips": null, "control_number_line1": "0012-0012N426", "control_number_line2": "0000000023-ELECTR", "employee_address": "636 CAMP DIXIE ROAD, PASCOAG RI 02859", "employee_name": "JOHN BLODGETT", "employee_ssn": "XXX-XX-XXXXX", "employer_address": "PO BOX 107, PASCOAG RI 02859", "employer_ein": "05-0517079", "employer_name": "PASCOAG UTILITY DISTRICT", "state_local_row_1": {"employer_state_id": "05051707900", "local_income_tax": null, "local_wages": null, "locality_name": null, "state": "RI", "state_income_tax": 49.7, "state_wages": 1325}, "state_local_row_2": null, "void": false}