-
Notifications
You must be signed in to change notification settings - Fork 9
XML and JSON Binding
Bound metadata types are annotated for Jakarta XML Binding and Jakarta JSON Binding, so a fetched result set can be written to a file, sent over a wire, or diffed between two database products without writing any mapping code.
Both APIs are declared provided and optional, so neither is on your classpath transitively. Add the API and a
provider for whichever you use:
| Binding | API | Provider used by this project's tests |
|---|---|---|
| XML | jakarta.xml.bind:jakarta.xml.bind-api |
org.glassfish.jaxb:jaxb-runtime |
| JSON | jakarta.json.bind:jakarta.json.bind-api |
org.eclipse:yasson |
Both bindings are field-based, which matters because binding types have private fields, package-private setters,
and protected constructors. The package configures the access mode itself, so a default provider instance works
without extra setup on your side.
Unknown columns are excluded from both bindings, exactly as they are excluded from the Java serialized form. See Model Notes for why.
XML output is namespace-qualified. The namespace is published as
JakartaXmlBindingConstants.NAMESPACE_URI = https://github.com/jinahya/database-metadata-bind.
Each bound type carries its own @XmlRootElement name — table, column, primaryKey, and so on; see the table in
API Reference.
The package ships a jaxb.index resource listing every bound class, so a JAXBContext can be created by package name
rather than by enumerating classes:
var jaxbContext = JAXBContext.newInstance(MetadataType.class.getPackageName());JAXB cannot marshal a bare java.util.List — a collection must be held by a type annotated with @XmlRootElement.
MetadataTypeWrapper<T> is that holder. One generic class serves every element type: the list is marshalled with
@XmlAnyElement(lax = true), so each element is written under its own root element name.
var wrapper = new MetadataTypeWrapper<Schema>();
wrapper.getElements().addAll(context.getSchemas());
var marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(wrapper, path.toFile());<metadataTypes>
<schema>...</schema>
<schema>...</schema>
</metadataTypes>MetadataTypeWrapper exposes a public no-argument constructor, a modifiable getElements() list (created lazily, so
it is never null), and the constant ROOT_ELEMENT_NAME = metadataTypes.
The trade-off of a single generic wrapper is that every document shares the one root element metadataTypes rather
than a per-type name such as <schemas> or <tables>. The element types must be known to the JAXBContext, which is
what bootstrapping by package name (and therefore jaxb.index) takes care of.
var object = jaxbContext.createUnmarshaller().unmarshal(path.toFile());
if (object instanceof MetadataTypeWrapper<?> wrapper) {
List<?> elements = wrapper.getElements();
}The type variable T is erased at runtime, so the unmarshalled wrapper is raw — cast the elements to the type you
wrote.
JSON binding needs no wrapper: JSON-B serializes a List directly to a JSON array.
try (var jsonb = JsonbBuilder.create()) {
String json = jsonb.toJson(context.getSchemas());
Schema schema = jsonb.fromJson("{\"tableSchem\":\"PUBLIC\"}", Schema.class);
}A default JsonbBuilder.create() instance both serializes and deserializes binding types correctly because the
package declares
@JsonbVisibility(JakartaJsonBindingUtils.FieldAccessVisibilityStrategy.class). That strategy makes all fields visible
and all methods invisible, so JSON-B binds directly to fields instead of going through the non-public accessors.
JakartaJsonBindingUtils.FieldAccessVisibilityStrategy is public only so a JSON-B provider can instantiate it
reflectively from the package annotation. Application code does not need to reference it.
Both bindings are exercised by the test suite rather than by any public helper on Context. The in-memory database
tests and the external integration tests run the same metadata walkthrough and write each collection to
target/{product}-{product-version}-{driver}-{driver-version}-{name}.xml and .json, which makes the artifacts easy
to diff across database products and driver versions. See Testing and Build and External Integration Tests.