Here's an idea that I've used on multiple projects with complex structures. Create a set of macros that can be used as a sort of "language" for declaring your struct. As a very simple example.
DEFSTRUCT( TypeARecord, "Type A Equipment Record" )
FIELD(id, int, 0, "6-digit I.D. Number" )
FIELD(weight, double, 0.0, "Item Weight")
STRING(name, 30, "", "Item Name")
ENDSTRUCT( TypeARecord )
Not pretty, and more information that you need for a struct declaration, but there's a reason. Put these lines into a separate file to be included, and then, where needed, you can do something like:
#define DEFSTRUCT(Name, Desc) typedef struct Name {
#define FIELD(Name, Type, Init, Desc) Type Name;
#define STRING(Name, MaxLen, Init, Desc) char Name[(MaxLen)+1];
#define ENDSTRUCT(Name) } Name;
#include "A_def.inc"
#undef DEFSTRUCT
#undef FIELD
#undef STRING
#undef ENDSTRUCT
That will expand to the usual struct type definition, with a small convenience of adding 1 to string lengths to allow for a terminating '\0'. However, with a different set of macro definitions in a different place, probably in a different source file, you can have those macros expand to a braced initializer list, or a set of printf() statements to produce program documentation, or scanf() format strings or whatever the simple syntax of macro expansion will let you do. They're easier to parse than XML too, so you can do more sophisticated processing by feeding those to a program.
I've used this a lot for something related: Managing a large set of enum definitions. In one include it generates the plain enum definition. In another, it generates an array of strings for converting enum index values to printable text, and in yet another it generates an array of function pointers, so that each enum value can have a custom "handler".
I've also used that idea to use the same set of macros to generate online help text for an embedded product, and separately to generate text for a printable manual.
It's not all that pretty, but it does provide a single point of maintenance, kind of a "data dictionary" in code. It's nice NOT having to worry about partial updates. A project rebuild always has everything in sync.