Groundwork Data Management

29 October 2026

Upcycling existing XML

Remember that feeling? You spent a lot of time on a really nice XML Schema, but then the content creators managed to find every shortcut and loophole. Good intentions gone awry

Schema design was not called "black magic" without reason. Yes, there were rules to follow (lots of rules, in case of XML Schema). But at the end of the day, schema design was always going to be a balancing exercise. Different stakeholders had different interests. Some functional domains required really detailed data descriptions, with lots of elements and attributes. The actual people doing the content creation were generally in favour of fewer elements. And then there were future requirements and changes that would necessitate new versions of schemas.

It was all up to the poor schema designer to make the most of it. And sometimes it would be easier to just adopt an international, domain-specific standard. But often that would have some subtle issues too, that reflected the opinions and context of those schema designers. A further complication with using an external schema would be conversions. Usually doable but more often than not an underappreciated (and underpaid) exercise.

In practice, most schema designers had to live with the trade-offs. You win some, you lose some. The result was that larger collections of XML instances might be technically schema-valid, while at the same time not following the intent of the schema designer. This was unavoidable. There were always production pressures, and schema validity soon became the ceiling instead of the floor in terms of quality. This wasn't always bad. There was usually enough in there to get the value out of using XML, but it never completely lived up to the promise of meaningful content and markup.

Take a simple example: a definitions article with a lettered list. The schema designer provided a list element for exactly this, so the intended markup looks like this:

<para>In this by-law:</para>
<list type="letters">
  <item>a. dog: a dog as referred to in section 2;</item>
  <item>b. keeper: the person who has a dog in their care;</item>
  <item>c. council: the municipal council.</item>
</list>

But a paragraph is also allowed here, and typing a letter at the start of a paragraph is quicker than inserting a list. So in a lot of documents, it looks like this:

<para>In this by-law:</para>
<para>a. dog: a dog as referred to in section 2;</para>
<para>b. keeper: the person who has a dog in their care;</para>
<para>c. council: the municipal council.</para>

Both versions can be schema-valid. On paper they look the same. But only the first one tells a computer that there is a list, where it starts and ends, and what its items are. The second one is an example of "typewriter coding": what you see is all you get. This might have been sufficient at the time of creation, but beware the project that has to write a smartphone app, or the new accessibility requirements that suddenly apply to all content including older stuff.

The right way is to upcycle the content, to make it match the schema intention. In smaller collections, this is sort of manageable, especially if you have knowledgeable people handling the work. The finding part can generally be automated with XPath or XQuery. However, the update part is where the trouble lies. There are likely to be many exceptions, like nested lists. Going through each case by hand becomes quite a chore quickly. There is also the risk that accidental errors are introduced. In many cases, scarce capacity meant that upcycling stayed on the back burner. And that is a small collection. With hundreds of thousands of documents, it is simply not feasible.

Fast forward to 2026.

The fundamental problem is that a human operator often sees in a second what would be really difficult to capture in code. But we don't want a human operator to go through each document one by one, because that would drive them crazy. Today, there is a solution to this problem. LLMs are quite good at figuring out what a text fragment means. Sometimes their ability is akin to magic. This opens a whole new approach. We can still use regular XPath or XQuery to find cases, but then we can ask an LLM if the text fragment actually means what we think it means. We can do this after our deterministic code has run, or we can design a pipeline where we first ask the LLM and then depending on the answer run the code.

That does not mean the LLM has to make the change itself. It might: one model makes the change and a second, stronger model judges it. I have not tried that yet, and I suspect it only works with a frontier model as the judge. But the easiest win does not need any of that. Let the code make the change, and let the LLM give a second opinion on it. If there are thousands of documents to inspect, having an LLM pick out the edge cases is the difference between being overwhelmed and being able to focus.

The main insight is that we don't have to feed an entire document to an LLM, but that we can focus on just the fragment, possibly with a little more context around it. And this focus allows us to use local LLMs. I have run some tests and common 14B parameter models perform well enough. This can be a cost-saver, but the biggest advantage is that the documents never leave the organisation. Local LLMs have come a long way, and it only requires a modest investment to get productive.

So how does this work in practice? We still have to find our candidates. That has not changed. For each candidate, we create a specific LLM request. This request contains general information and the context of this specific candidate. The answer is binary: "looks good" or "look again". A free-text answer cannot be counted; two fixed answers can. For our lettered list, one request could look like this:

The paragraphs below follow the lead-in: "In this by-law:"

<para>a. dog: a dog as referred to in section 2;</para>
<para>b. keeper: the person who has a dog in their care;</para>
<para>c. council: the municipal council.</para>

Do these paragraphs look like items in a lettered list?
Answer "looks good" or "look again".

The key point is that the question is focused. It asks one thing (only) and it contains everything the model needs to reason about an answer. If a single XML document has several candidates, each of them gets its own LLM request and its own answer.

Best of all, this can really make a dent in upcycling the markup of a large XML collection, without getting overwhelmed. The process is as follows. There is a query that finds the candidates, code that fixes them, and the LLM check. The LLM finds the interesting edge cases: the "look agains". Based on those cases, the query or the code is improved, and the LLM check runs again. Each round, the "look again" pile gets smaller, and what is left at the end is genuinely unusual. Some of those edge cases are so far out that they are set aside for manual improvement. That could still be a fair bit of work, but at least it is a lot more focused.

For years, upcycling stayed on the back burner, because the exceptions made it too expensive. That is no longer true. The schema designer's intent is still there, in the schema, waiting. With the LLM picking out the edge cases, we can finally bring a large collection up to it, and schema validity can go back to being the floor.

All articles

Questions or comments about this article? Send me an email.