All posts

A desktop GUI for generating FAQPage JSON-LD - paste questions in, get schema out

The most-requested schema type in client work is FAQPage. Hand-writing it gets tedious by the third one. A small Tkinter GUI that accepts copy-pasted Q/A text in any format, parses the questions and answers, and writes valid JSON-LD to disk.

If you've worked on schema markup for clients, you know the rhythm. Content writes the FAQs in a Google Doc. Somebody needs to convert them to schema-compliant JSON-LD. The conversion is mechanical but error-prone - one missed closing brace and Google's Rich Results Test rejects the whole block.

I got tired of doing it by hand. So I built a tiny desktop GUI that does it in three clicks.

What it does

The GUI is two text fields and a save button:

  1. A big text box for the raw FAQ content. Paste anything that follows a Q: / A: structure (or Q. / A., or Question: / Answer:, or any reasonable variation).
  2. A small text input for the optional source URL.
  3. A file-format radio (JSON or TXT) and a Save button.

Click Save, point it at a folder, and you get a valid FAQPage JSON-LD block ready to drop into a page's <head>.

The format-flexibility detail

The reason this GUI exists rather than a CLI script is that the input format is never the same twice. One writer uses "Q:" and "A:". Another uses "Question 1." and "Answer 1." Another uses just numbered lines. Hand-parsing each variant into the same JSON output gets old fast.

The regex catches all the common patterns:

re.match(r'^(Q:|Q\.|Question\.?|Question:)[\s\t]*', line, re.I)
re.match(r'^(A:|A\.|Answer\.?|Answer:)[\s\t]*', line, re.I)

Anything matching the question pattern starts a new entry. Anything matching the answer pattern closes the current entry and writes it to the mainEntity array. Empty lines and unrelated text are skipped.

The case-insensitive flag and the . vs : flexibility cover about 95% of the format variations writers use in real content. The remaining 5% I just fix by hand in the text box before hitting Save - faster than expanding the regex to handle every conceivable input style.

Why Tkinter instead of a web UI

Two reasons.

First, Tkinter ships with Python. No dependencies, no Node.js, no npm install, no localhost server. Double-click the .py file and it runs.

Second, this lives on my laptop and runs offline. Generating FAQ schema doesn't need a backend, doesn't need to talk to an API, and doesn't need a database. A Python script with a 50-line GUI is the right scale for the job. Anything bigger - a Flask app, a static site with JS, a web service - would be overengineering for what is fundamentally a one-person utility.

Tkinter is also fast enough. The GUI launches in under a second. Compared to a web app that needs to spin up a server, open a browser, and load a page, Tkinter is genuinely the right choice for a single-user desktop tool.

The output

For input like:

Q: Do you ship to Canada?
A: Yes, we ship to all Canadian provinces with delivery in 3-5 business days.

Q: What is your return policy?
A: 30-day full refund on unworn items. Contact [email protected] to start a return.

The script writes a clean JSON-LD block:

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "url": "https://example.com/faq",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Do you ship to Canada?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes, we ship to all Canadian provinces with delivery in 3-5 business days."
      }
    },
    {
      "@type": "Question",
      "name": "What is your return policy?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "30-day full refund on unworn items. Contact [email protected] to start a return."
      }
    }
  ]
}

That goes straight into the page's <head> wrapped in a <script type="application/ld+json"> tag and Google's Rich Results Test passes it without complaint.

What I would change

A few iterations worth doing on the next pass:

Live validation. Right now the script writes the JSON file and trusts that it's valid. A small jsonschema check against the FAQPage spec would catch malformed inputs before they hit the file system.

Direct paste-to-clipboard mode. Half the time the JSON is going straight into a CMS editor, not into a file. A "Copy to clipboard" button alongside Save would skip the disk round-trip.

HTML wrapper option. Toggle that wraps the JSON-LD in the <script type="application/ld+json">...</script> tags ready to paste. Today I do that wrapping manually because I forget half the time.

Multi-FAQ batch mode. Some pages have multiple FAQ sections. Right now I run the tool once per section. A "process multiple FAQs from the same input" option would handle pages with five separate FAQ blocks at once.

But for the actual job - "take some FAQ content from a writer and turn it into schema" - the GUI has shipped dozens of FAQPage blocks across client work without a single Rich Results Test rejection. The tool is small. The work it removes from my plate is not.

Source: github.com/schandler7171/portfolio-example-scripts/tree/main/FAQSchema-SEOOn-Page