Skip to content

Toolset aimed at providing some added ease-of-use to the XML APIs of the JDK.

License

Notifications You must be signed in to change notification settings

digipost/digipost-xml

Repository files navigation

Apache 2.0 License Build and deploy

Digipost XML

Toolset aimed at providing some added ease-of-use to the XML APIs of the JDK.

Digipost XML consists of the following three libraries:

  • digipost-xml-fundamentals digipost-xml-fundamentals javadoc: offers some ergonomics on top of the standard XML APIs in the JDK. The baseline JDK requirement for this is Java 8, and there are not additional dependencies.

  • digipost-xml-bind-jakarta digipost-xml-fundamentals javadoc: JAXB facilities, inspired by Spring OXM, and in addition contains a library of some useful adapters and other JAXB-related stuff.

  • digipost-xml-bind-javax digipost-xml-fundamentals javadoc: This is identical to the above, but for the legacy javax.xml.bind namespace.

Who is this library for?

This library is primarily intended for other libraries, where you may not wish to introduce a dependency to Spring. There is nothing wrong about Spring, and this library makes no effort to hide its heavy inspiration by Spring’s own Jaxb2Marshaller, but to use that you would need to depend on spring-oxm, which further depends on both spring-beans and spring-core, and as a library author you would want to keep your dependencies as clean and focused as possible to avoid introducing unnecessary artifacts into your library consumer’s dependencies.

The library is of course also applicable for applications/servers if you use frameworks/libraries not already offering support for JAXB, or you need to interact somewhat directly with the XML APIs in the JDK.

There is also the a bit specific case where you need to support JAXB marshalling using both the new Jakarta EE XML Bind API, and the legacy javax.xml.bind namespace. It is, contrary to popular belief, possible to use these two JAXB variants side-by-side, should you need to, thanks to the almighty jaxb-resolver-com.sun.xml.bind.

Towards version 1.0

The short-term plan at this point is to release a Release Candidate version for use by a service offered at Posten Bring, and followingly do a final 1.0 release when we are able to "validate" its usability across different use-cases in different applications. You are most welcome to try it out, and if it seems to be applicable for your use-case, we would love to hear about it, and if you have some suggestions to potentially make it a better fit for your and/or other use cases!

How to use

Declare the following in your dependencyManagement section

<dependencyManagement>
    <dependency>
        <groupId>no.digipost.xml</groupId>
        <artifactId>digipost-xml-bom</artifactId>
        <version>[latest-version]</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    ...
</dependencyManagement>

And depend on the part(s) you need. E.g:

<dependency>
    <groupId>no.digipost.xml</groupId>
    <artifactId>digipost-xml-fundamentals</artifactId>
</dependency>
<dependency>
    <groupId>no.digipost.xml</groupId>
    <artifactId>digipost-xml-bind-jakarta</artifactId>
</dependency>

Substitute "jakarta" with "javax" to digipost-xml-bind-javax to use the legacy javax.xml.bind variant, or even include both if you need to support both variants in your application, as they can happily coexist.

Digipost XML Fundamentals

The XML APIs of the JDK is admittedly very low-level, and offer flexible access to the excellent XML facilities in Java. Usually you do not access these APIs directly, but they are used implicitly by libraries or frameworks. Should you need to interact directly with these APIs, this library may offer some ergonomics.

Secure XML parsers

SAX parsers can not be shared across threads, because they modify their internal state per parsing task. So you obtain a parser each from a SAXParserFactory each time you are going to parse som XML. Both initializing the factory and using it to create a parser are pestered with checked exceptions.

SaxParserProvider is just the same factory pattern for obtaining a SAX parser, but without being forced to handle a smörgåsbord of checked exceptions. In addition, a ready-made secure SAX parser provider is offered through SaxParserProvider.createSecuredProvider(), which yields parsers configured to align with OWASP recommendations.

XML validation with schemas

Creating instances of javax.xml.validation.Schema is something you usually do with static resources you already own. Yet, just pointing to some XSDs you have on classpath to parse and create a Schema to use for validating XML you either create or consume, is ridiculously hard.

SchemaHelper.createW3cXmlSchema(Collection<String> schemaResourceNames) takes a collection of classpath resource names (commonly XSD files you bundle with your application), and gives back an instance of Schema.

Digipost XML Bind (JAXB)

The XML Bind modules are offered as two variants: one for the current Jakarta namespace, and one for the legacy javax.xml.bind namespace of JAXB. If you have the need to e.g support older JAXB-generated classes as well as the current JAXB version using jakarta.xml.bind, these two libraries should be able to happily coexist and operate within the same application.

JaxbMarshaller

The main purpose of this library is to offer the JaxbMarshaller class which is inspired by Jaxb2Marshaller in Spring OXM. It offers thread-safe access to marshalling and unmarshalling facilities without having to worry about which component of the XML Bind (JAXB) is thread-safe, i.e. which instances that should be shared, and which should be created on each use. A JaxbMarshaller can be shared across threads, as one would expect.

JaxbMarshaller offers an API for typical configuration use cases: - which classes which are bound to the JAXB context - schemas used for validation, and if used for either unmarshalling, marshalling, or both - other arbitrary custom configuration of Marshaller and Unmarshaller instances

Examples

Validate against schema bundled with your application on both marshalling and unmarshalling, and set a custom property for marshalling:

var marshaller = new JaxbMarshaller(
    MarshallingCustomization
        .validateUsingSchemaResources(Set.of("/xsd/my-schema-on-classpath.xsd"))
        .andThenOnMarshalling(marshaller -> marshaller.setProperty("jaxb.formatted.output", true)),
    ABoundClass.class, AnotherBoundClass.class);

Alternatively, often it is advisable to not do a formal schema validation on XML consumed from an API response, as long as the unmarshaller is able to parse and map to your classes, as this enables the service to introduce changes to the responses in a backwards compatible manner. Say to introduce new error codes in a schema-defined enumeration, which clients may or may not support spesific handling for, or introduce new elements which clients are strictly not required to consume. A client making requests to a server with a server-defined schema, should most of the times validate the marshalled XML before sending it to the server.

var marshaller = new JaxbMarshaller(
    MarshallingCustomization
        .onMarshalling(MarshallerCustomizer
            .validateUsingSchemaResources(Set.of("/xsd/my-schema-on-classpath.xsd"))
            .andThen(marshaller -> marshaller.setProperty("jaxb.formatted.output", true)))
        .andThenOnUnmarshalling(unmarshaller -> {
                // anything you want to do on the unmarshaller?
                // You can also supply UnmarshallerCustomizer.NO_CUSTOMIZATION to be
                // explicit, or just omit invocation of .andThenOnUnmarshalling(..)
            }),
    ABoundClass.class, AnotherBoundClass.class);

The JaxbMarshaller instance offers methods to either marshal (generate XML from Java objects) or unmarshal (parse XML and map contents to a Java object).

About

Toolset aimed at providing some added ease-of-use to the XML APIs of the JDK.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages