1

I work for a company that uses XML for storing a bunch of serialized Java classes. The framework we use is Apache Betwixt (abandonded in 2008) and it's kind of pain to maintain and extend (it seemed like a good choice back in the day).

Crash course in Betwixt along with the detailed problem:

  • Only supports public getters and setters, both getter and setter have to be present
  • Does not support JAXB-style type adapters
  • The XML evolves with the Java objects, resulting in redundant methods in our classes

Our list of xml serializable classes is well over 150, and the codebase can get very messy when changes to definitions are made. Since the XML is bound to Java getters and setters, we can only migrate XML by keeping old properties in the class definitions.

NowTake a look at this change:

public class ThingWithText {       
  private String text;

  public String getText() {
     return text;
  }

  public void setText(String text) {
     this.text = text;
  }
}

The resulting XML looks something like:

<thingWithText>
    <text>Whatever</text>
</thingWithText>

Now consider a change in the model to add multiple locales to the ThingWithText:

public class ThingWithText {
   private List<TextWithLanguage> texts = new LinkedList<TextWithLanguage>();

   // Getter must be here so the framework recognizes the property. 
   public String getText() {
      return null;
   }

   // Has to be here so it can be serialized
   public void setText(String text) {
      this.texts.add(new TextWithLanguage(text));
   }

   public List<TextWithLanguage> getTexts() {
      return texts;
   }

   public void addText(TextWithLanguage text) {
      texts.add(text);
   }
}

public class TextWithLanguage {
   private Locale locale = // omitted;
   private String text;

   public TextWithLanguage(String text) {
      this.text = text;
   }

   // Getter and setter omitted
}

The XML now looks like:

<thingWithText>
    <texts>
        <text locale="en">Whatever</text>
    </texts>
</thingWithText>

Now the XML isn't bad looking or anything, but it still leaves the ThingWithText class with several problems:

  • A setter for a non-existing field
  • A getter that always returns null, may cause exceptions because stuff still compiles

My questions go:

We have made the decision to do something about this, and it seems like a problem other people would have.

  1. How does one handle migration between an old framework like Betwixt to a newer (JAXB, Xstream, whatever)?

  2. If we were to keep our framework: How can we manage the considerable code complexity added by null methods and fake setters?

2

How does one handle migration between an old framework like Betwixt to a newer (JAXB, Xstream, whatever)?

Make a new implementation for your data access layer. If you don't have an explicit data access layer, this would be a good time to introduce one! Stores will go to the new implementation, loads will come from either the new, if possible, or the old, if necessary.

dataaccess
  +-- betwixt
  +-- jaxb_xstream_whatever

Some may argue that the whole DAO/DTO approach is ugly and leads to code bloat and duplication (I happen to be one of those people). But I've often needed it, and it has saved projects large and old.


(Disclaimer: I had never heard of Betwixt before today, and had only a cursory look at the documentation.)

If we were to keep our framework: How can we manage the considerable code complexity added by null methods and fake setters?

Ideally, by changing the data so that it doesn't need them. If you find yourself with a format change that is difficult to marry with a previous format, migrate all your data to the new format. Your application already does this, piecewise, by supporting the previous format. You're working with XML, so it may be as simple as a few XSL transformations.

If you can't migrate the data to a new format en masse, you can continue to support the previous format as long as you can make the getters and setters sensical (or at least deterministic), possibly deprecated methods. Either make them useful, or make them throw exceptions: if setText does not throw an exception when I call it, I expect getText to return me a sensical result.

Lastly, if the format changed sufficiently that you cannot reliably support both formats in a single class, then either you're trying to shoehorn new concepts into old names, and you should really create a different data type, or the format represents a new generation of data types, and should supersede the previous class, with the previous clearly marked as such (ThingWithText_v1_8).

From what I gathered from a quick glance at the Betwixt documentation, they support creation chains and multiple mappings. My guess is they could help you support multiple formats for as long as is really necessary, but I have no clue whether that would give something not-too-fragile.

  • +1 for XSL transformation suggestion. I think this is clearly the way forward: come up with a set of XSLTs that transform the old format XML into a newer, leaner version, so that you can lose all the old backward-compatibility methods. Then, whenever you want to change it in future, just add an appropriate transformation so that you never need to keep anything in the old version again. This is exactly why I prefer XML serialization to other formats -- I don't need to worry about backward compatibility in my code, I can have a simple separate layer for that. – Jules Nov 14 '14 at 19:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.