Extract Messy Text into JSON with AI and Validate the Result in Python
AI can turn unstructured notes into structured JSON, but the result still needs validation. This guide uses a tiny synthetic example and a Python schema check to verify required fields, types, allowed values, and basic consistency.
Content checked 2026.09.21Example files included
Show contents
Who this is forPeople who use an AI chat assistant to convert notes, emails, forms, or other messy text into structured JSON and need a repeatable validation step.
What you need
Python 3.12
Basic familiarity with JSON objects, strings, numbers, and lists
01Why validate AI-extracted JSON
AI is useful for turning irregular text into structured fields, but a response that looks like valid JSON can still be wrong. A required key may be missing, a number may be returned as text, a field may contain an unsupported value, or the AI may infer information that was never present in the source.
The safest workflow separates extraction from validation. First ask the AI to map the source text into a fixed structure. Then check the returned JSON against explicit rules. Validation does not prove that every extracted fact is correct, but it catches many structural errors before the data is copied into a spreadsheet, database, script, or workflow.
02Define a small synthetic source and schema
This example is synthetic. Suppose a short service note contains a customer name, ticket number, priority, affected products, and an optional callback time.
Source text: "Ticket 1842. Customer: Mira Lee. Login fails on the desktop app and web portal. Priority is high. Please call after 15:30. Products affected: Desktop, Web."
Field
Rule
ticket_id
Required integer
customer
Required non-empty string
priority
Required: low, medium, or high
products
Required non-empty list of strings
callback_time
String in HH:MM form or null
The schema is deliberately simple and can be checked with the Python standard library. The rules are stricter than merely asking whether the response can be parsed as JSON.
03Ask the AI for a constrained JSON object
Tell the AI exactly which fields are allowed and how to handle missing information. For example: "Extract the following text into one JSON object with exactly these keys: ticket_id, customer, priority, products, callback_time. ticket_id must be an integer. priority must be low, medium, or high. products must be a list of strings. Use null for callback_time if it is not stated. Do not infer missing facts. Return JSON only."
A correct result for the synthetic note would be: ticket_id 1842, customer Mira Lee, priority high, products Desktop and Web, and callback_time 15:30.
A plausible but defective AI result might instead return ticket_id as the string "1842", priority as "urgent", and add an unsupported field such as issue_summary. It may still look structured and readable, but it does not satisfy the agreed schema.
04Validate the JSON with Python
The following script uses only the Python standard library. It loads a synthetic AI response, checks that no unexpected keys are present, verifies required keys and types, tests the allowed priority values, checks the product list, and validates the callback time format. It writes a report to outputs and stops if the report already exists.
python
import json
import re
from pathlib import Path
AI_RESPONSE = '''{
"ticket_id": "1842",
"customer": "Mira Lee",
"priority": "urgent",
"products": ["Desktop", "Web"],
"callback_time": "15:30",
"issue_summary": "Login problem"
}'''
REQUIRED_KEYS = {
"ticket_id",
"customer",
"priority",
"products",
"callback_time",
}
ALLOWED_PRIORITIES = {"low", "medium", "high"}
TIME_PATTERN = re.compile(r"^(?:[01]\d|2[0-3]):[0-5]\d$")
errors = []
try:
data = json.loads(AI_RESPONSE)
except json.JSONDecodeError as exc:
raise SystemExit(f"Invalid JSON: {exc}")
if not isinstance(data, dict):
errors.append("Top-level value must be a JSON object.")
else:
actual_keys = set(data)
missing = REQUIRED_KEYS - actual_keys
unexpected = actual_keys - REQUIRED_KEYS
if missing:
errors.append("Missing keys: " + ", ".join(sorted(missing)))
if unexpected:
errors.append("Unexpected keys: " + ", ".join(sorted(unexpected)))
if "ticket_id" in data and not isinstance(data["ticket_id"], int):
errors.append("ticket_id must be an integer.")
if "customer" in data:
customer = data["customer"]
if not isinstance(customer, str) or not customer.strip():
errors.append("customer must be a non-empty string.")
if "priority" in data:
priority = data["priority"]
if not isinstance(priority, str) or priority not in ALLOWED_PRIORITIES:
errors.append("priority must be low, medium, or high.")
if "products" in data:
products = data["products"]
if not isinstance(products, list) or not products:
errors.append("products must be a non-empty list.")
elif not all(isinstance(item, str) and item.strip() for item in products):
errors.append("Every product must be a non-empty string.")
if "callback_time" in data:
callback = data["callback_time"]
if callback is not None:
if not isinstance(callback, str) or not TIME_PATTERN.fullmatch(callback):
errors.append("callback_time must be HH:MM or null.")
status = "PASS" if not errors else "FAIL"
lines = [f"Validation: {status}"]
lines.extend(f"- {error}" for error in errors)
output_dir = Path("outputs")
output_dir.mkdir(exist_ok=True)
output_file = output_dir / "json_validation.txt"
if output_file.exists():
raise SystemExit(f"Stop: {output_file} already exists.")
output_file.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f"Wrote {output_file}")
05Interpret structural validation correctly
For the defective synthetic response, validation should fail for three reasons. ticket_id is a string instead of an integer. priority is urgent, which is outside the allowed set. issue_summary is an unexpected key. The products list and callback_time format satisfy their structural rules.
Check
AI value
Expected
Result
ticket_id
"1842"
integer
FAIL
customer
"Mira Lee"
non-empty string
PASS
priority
"urgent"
low, medium, or high
FAIL
products
["Desktop", "Web"]
non-empty string list
PASS
callback_time
"15:30"
HH:MM or null
PASS
extra keys
issue_summary
none
FAIL
A schema failure should normally send the result back for correction rather than silently coercing it. Automatically converting "1842" to 1842 may hide the fact that the extraction instructions were not followed. Whether coercion is acceptable depends on the downstream workflow.
06Check extracted values against the source text
Structural validation is only one layer. You should also compare important fields with the original text. In this example, ticket 1842, Mira Lee, high priority, Desktop, Web, and 15:30 all appear explicitly in the source. The source does not use the word urgent, so replacing high with urgent changes the stated value rather than merely reformatting it.
Confirm identifiers and names against the exact source text.
Check that enum-like fields such as priority use the source meaning and the allowed schema value.
Verify that list items were actually mentioned rather than inferred.
Use null for optional fields that are absent instead of guessing.
Reject added fields unless the schema explicitly allows additional properties.
Distinguish normalization from invention; changing capitalization may be acceptable while adding a missing deadline is not.
07Common mistakes and limits
A common mistake is checking only whether json.loads accepts the response. Successful parsing proves that the syntax is valid JSON, not that required keys, types, or values are correct. Another mistake is allowing arbitrary extra fields, which can make downstream code depend on information that was never part of the specification.
For larger schemas, hand-written validation code becomes harder to maintain. A dedicated schema system or validation library may be appropriate, but the same basic questions remain: which fields are required, what types are allowed, what values are permitted, and whether missing information may be represented as null.
Validation also does not establish that the original text is complete or trustworthy. If the source itself contains an incorrect ticket number or ambiguous wording, a structurally correct extraction will preserve that problem. Keep the original source available so important fields can be traced back when a validation or business rule fails.
Execution and verification record
2026-09-21 · hand-checked example · Python 3.12
Checked the synthetic source text contains ticket 1842, customer Mira Lee, priority high, products Desktop and Web, and callback time 15:30.
Checked that the intended schema contains exactly five allowed keys: ticket_id, customer, priority, products, and callback_time.
Checked that the defective sample uses the string "1842" instead of integer 1842.
Checked that urgent is outside the allowed priority set low, medium, high.
Checked that issue_summary is an unexpected key under the stated schema.
Checked that ["Desktop", "Web"] satisfies the non-empty list-of-strings rule.
Checked that 15:30 matches the stated 24-hour HH:MM pattern.
Checked that the script writes only to outputs/json_validation.txt and stops if that file already exists.
Verification limits
The Python code was not executed by me; the synthetic example, validation branches, and expected failures were checked by inspection.
This hand-written validator covers only the small schema shown here and is not a general JSON Schema implementation.
Structural validation cannot prove that extracted values are factually correct; important values still need comparison with the source text.
The example does not automatically send failed output back to an AI for correction or perform security filtering on arbitrary input text.
Turn “automate this” into an executable task description. Attach a synthetic sample with no sensitive data and a hand-checked expected result to complete a request for a script that totals work logs by team.
Check an aggregation function with four rows you can calculate by hand and 12 unit tests. Verify not only normal values but also empty input, zero, decimals, and invalid input.
Split a plausible summary into facts, calculations, and interpretations. Recalculate ratios and averages from synthetic monthly data, and rewrite sentences with missing sources or overstated causes into checkable statements.
Treat an AI-generated regex as a draft, not a finished rule. Build a small synthetic test table, compare expected and actual matches, revise the pattern, and save a review report before using it on real data.