Best way to handle identical data within two different Java types generated from XSD files

114 views Asked by At

I am pretty much stuck finding the best way to solve this.

My case is that I have a provider which gives me XSD files to generate the types I will use to call their SOAP api. Those XSD files sometimes contain the exact same data structure. However as they reside in different XSD files they will generate their own types, even if the fields of those types have the exact same name.

The end goal is to be able to handle one type, so that I can write reusable logic for them.

I am using the com.helger.maven jaxb40-maven-plugin (fork from highsource jaxb-tools) along with Java 17 to generate Java classes from these XSD files which I am then building up, marshalling and sending as payload to their SOAP API as well as using to map the response from them.

I have been looking into:

  1. Episodes, but I am not getting that to work. My understanding, and I can be wrong, is that I need to modify these XSD files & creating a new XSD file with all the common types & reference those instead. However I don't want to mess with these XSD files because I think it's error prone, also they are like 15.000 lines long with loads of type so it's not feasible (if this is the case).

  2. Mapping from one type to the other using a mapping library like MapStruct/ModelMapper. I am struggling to make this work too. The Java classes I am generating I have configured to have optional getters & builders. This seems to make things a lot harder.

  3. Marshalling my class to XML, modyfing the namespace & root element name to be able to unmarshal it into my other class. However this feels like a hack, and again I am messing with the XML structure.

At this point I am not sure what is the best solution for my problem, hence I am reaching out here for some advice.

If it makes any difference I am using Spring Boot.

Thanks in advance,

BR

1

There are 1 answers

6
Laurent Schoelens On

Solution 1 :

I think the best option for you is to use the jaxb-tools inheritance plugin (link to the wiki). You could define your own interface (in your java code) and, with bindings file, say that the common classes are implementing the same interface. That way, you could create your unique handle java method and respect JAXB namespaces without any change to the XSD.

You would have something like that :

<jaxb:bindings
    xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:inheritance="urn:jaxb.jvnet.org:plugin:inheritance"
    jaxb:extensionBindingPrefixes="inheritance"
    jaxb:version="3.0">

  <jaxb:bindings schemaLocation="schema1.xsd" node="/xsd:schema">
    <jaxb:bindings node="xsd:simpleType[@name='MyCommonType']">
      <inheritance:implements>my.internalpackage.CommonTypeInterface</inheritance:implements>
    </jaxb:bindings>
  </jaxb:bindings>
  <jaxb:bindings schemaLocation="schema2.xsd" node="/xsd:schema">
    <jaxb:bindings node="xsd:simpleType[@name='MyCommonType']">
      <inheritance:implements>my.internalpackage.CommonTypeInterface</inheritance:implements>
    </jaxb:bindings>
  </jaxb:bindings>
</jaxb:bindings>

You can also check the migration guide if looking for new artifacts / groupId names since a lot changed this year in jaxb-tools repository.

Feel free to create an issue if you got any problem with that plugin or if the wiki is uncomplete / unclear about certain points. I'd be glad to enhance it.

Solution 2 :

Another option would be to ask the provider to create a common.xsd which would contains the common XSD types that will be used in others XSD structure. That way, you'll be able to handle it correctly with JAXB and only have one handling method without having this inheritance plugin.

Solution 3 :

If you can't get your XSD provider change it's XSD and don't want to get into inheritance plugin, I'd suggest you to look at some automatic mapper framework. Selma is a pretty good one I use sometimes to do automatic conversion on similar classes. It generates by itself a mapper that will do a deep-map field-by-field from one type to another.

For example, by defining the following methods in the selma class

public abstract my.choosen.type.MyCommonType asCommonType(another.type type);

public abstract my.choosen.type.MyCommonType asCommonType(yet.another.type type);

You'll get the following in the Selma Generated Class :

  public final my.choosen.type.MyCommonType asCommonType(another.type inType) {
    my.choosen.type.MyCommonType out = null;
    if (inType != null) {
      out = new my.choosen.type.MyCommonType();
      out.setA(inType.getA());
      out.setB(inType.getB());
    }
    return out;
  }


  public final my.choosen.type.MyCommonType asCommonType(yet.another.type inType) {
    my.choosen.type.MyCommonType out = null;
    if (inType != null) {
      out = new my.choosen.type.MyCommonType();
      out.setA(inType.getA());
      out.setB(inType.getB());
    }
    return out;
  }

And then work only with the my.choosen.type.MyCommonType in your code.