Inheritance with JAXB - java

I have an XSD file which is used to generate some objects which are then shared with other systems. I'd like to customize some of the business logic a bit in there by making some more specific implementation of these. I'm not adding new fields which need to be serialized, but more along the lines of adding setMethods which may take different format parameters. These methods do the work of translating the data into a form which is needed by the underlying object.
I may have a field on the JAXB object which is a string, but my system gives me an integer. So, I want to handle the work of converting that in a class which extends my base class.
So, is there anything special you need to do in order to get JAXB to look for XmlRootElement on a subclass of the object you are asking it to serialize? Or must I include a #XmlRootElement attribute on my more specific class?
thanks!

Yes, #XmlRootElement/ #XmlType should be enough. But why don't you just add these methods to the generated classes? (in case you are not regenerating them on each build, which is wrong imo)
However I'd advice externalizing the conversion to other (converter) classes / methods.

Related

How to generate serialVersionUID-like id for a Java Class at runtime?

I'd like to generate an Id for a given Java Class by inspecting its field names, types, and all other components of the class that play a role during serialization and deserialization, not necessarily Java serialization as it could be Json, etc.
The id generation should be repeatable and deterministic unless there's a change in a field type or some other component of the class. I think I could recursively traverse the class and generate something like it but I didn't want to do it unless there's no other way.
I read that Java applies a complex procedure to determine serialVersionUID if none is declared. It considers class members, its super interfaces, etc. It seemed similar but I'd like to generate ids for non-Serializable classes as well.
My use-case is that I'm storing Java object(s) in an external datastore and I'd like to incorporate this runtime-generated id in the key to handle non-backward compatible changes gracefully.
You can get exactly the serialVersionUID of any class whether the field is present or not, via ObjectStreamClass.forName(String).getSerialVersionUID().

Java: Using 2 or more Classes for Freemarker Data Model processing

I've got a problem with understanding the Freemarker Data Model and how to work with it correctly.
I currently have 1 Core Data Class.
It is stuffed as hell (a lot of variables with getters and setters) and I want to split it into different Classes.
But my Core Class is fueling a Freemarker processing... And I just don't get, how to get this working with several classes.
Currently I use this:
templateValues.process(config_generator.CDATA, stringWriter);
CDATA is my Core Data Class...
I checked the Freemarker guide several times, but I just don't get it.
If I would implement 2 Classes (CDATA1 & CDATA2 i.e.) how would I set up the processing? And would this work with even more Classes as well? (CDATA3 to X).
There's no such feature out of the box, but it's just the question of the TemplateHashModel implementation you pass in as the data model. (The data model is always a TemplateHashModel, though if you pass in a POJO FreeMarker will adapt it automatically via the ObjectWrapper in the Configuration.) You could easily write a TemplateHashModel implementation that encloses a list of other TemplateHashModel-s, and whose get(String) returns the requested variable from the first enclosed TemplateHashModel whose get(String) returns non-null. To adapt your POJO (CDATA1 etc.) to TemplateHashModel call cfg.getObjectWrapper().wrap(myObject), where cfg is the freemarker.template.Configuration object.

Derived types as part of XML messages

We have created a couple of XML-Schemas where a field refers to an abstract type, but at runtime the messsage will contain a type derived from that abstract type. The Java code handles that properly by default, but I am puzzled, if and how the XML-unmarshalling will handle that. Will the Java<=>XML code created by JAXB be able to handle that out of the box or do we have to do some handholding?
This is a somewhat abstract question so here's a somewhat abstract answer.
JAXB will most probably be able to handle that:
Specific type can be specified via xsi:type. Please see this post by Blais Doughan. This method allows using a specific type in the runtime. You'll get the same element but the xsi:type will specify the specific type used. For instance you could get <geometry xsi:type="polygonType" .../>
Sometimes inheritance is modeled using substitution groups, see the #XmlElementDecl substitutionHeadName/substitutionHeadNamespace. In this case you could replace an abstract element with a specific element. For instance an abstract _Geometry element with a specific Polygon element.
JAXB supports both methods but through different constructs.

How to determine that data previously unmarshalled with JAXB was changed?

I only want to marshall the data with JAXB to a file if the previously unmarshalled data has been changed by the user. I know that classes generated by JAXB don't have equals() method. Is there any simple way to determine whether the data has been changed after unmarshalling?
E.g. org.w3c.dom.Document has isEqualNode() method for this purpose.
So, JAXB does not produce classes it marshalls data from instances of classes or populates instances by unmarshalling xml. Usually the classes are generated prior to compile-time by XJC. One option is to update the classes to include an equals method. This is not a great idea since you generally don't want to update generated code in case you need to regenerate at a later date. So you could write a utility class that takes two instances of the classes and compares them.
This is for comparing after unmarshalling. Another option would be to perform a check prior to unmarshalling by doing a checksum on the file.
The best is probably to implement Comparator or utility classes to check equality.
You could for instance rely on commons-lang EqualsBuilder and CompareToBuilder.
One fairly standard approach is to add a transient boolean to the class, isDirty, and your setter methods will set that to true. (or, if you prefer, add a dateModified)
Obviously, this requires changing your internal class code which may be inappropriate.
You could also keep some Set of all objects that have been modified. But getting this logic correct may also be tricky or impossible depending on how your code is organized.
You can use the JAXB2 Basics Plug-in to have equals methods generated into your model classes:
http://confluence.highsource.org/display/J2B/JAXB2+Basics+Plugins
I have decided to marshal to a org.w3c.dom.Document and to use its isEqualNode()-method. If the original document differs from the new one then I marshal to a file. As I haven't so much XML data it works for me.

Java serialization, UID not changed. Can I add new variables and method to the class?

I have a class that is serialised. Now I need to add a new variable into the class, with setter and getter methods. This class is sent over wire in RMI.
Without changing the UID, can I add new parameters and getter and setter methods for it? I tried to write an example class that is sent over wire, and did not change the UID, and added new parameters and getter and setter methods for it. On the other end, I tested it and I still got the values properly. I had assumed, if I add new parameters, getter and setter methods, I need to change the UID. Am I wrong?
If you hard-code the SerialVersionUID of a class, (to 1L, usually), store some instances, and then re-define the class, you basically get this behavior (which is more or less common sense):
New fields (present in class definition, not present in the serialized instance) are assigned a default value, which is null for objects, or the same value as an uninitialized field for primitives.
Removed fields (not present in class definition but present in the serialized instance) are simply ignored.
So the general rule of thumb is, if you simply add fields and methods, and don't change any of the existing stuff, AND if you're OK with default values for these new fields, you're generally OK.
Wow, a lot of bad information.
Java serialization is +very+ robust. There are a very well defined set of rules governing backwards compatibility of objects with the same uid and different data. the basic idea is that as long as you don't change the the type of an existing member, you can maintain the same uid without data issues.
that said, your code still needs to be smart about handling classes with potentially missing data. the object may deserialize correctly, but there may not be data in certain fields (e.g. if you added a field to the class and are deserializing an old version of the class). if your code can handle this, than you can probably keep the current uid. if not, then you should probably change it.
in addition to the pre-defined rules, there are advanced usage scenarios where you could even change the type of existing fields and still manage to deserialize the data, but that generally only necessary in extreme situations.
java serialization is very well documented online, you should be able to find all this information in the relevant sun/oracle tutorials/docs.
This only matters if you let Java generate a default UID for your class. It uses the actual members and methods of the class to generate it, thus making it invalid once you change the class structure. If you provide an UID for your class then this only matters if you need to deserialize older versions of your class from a file and such.
Want to define few point to highlight the changes which impacts serialization.
Below you will find the link to Oracle Java Docs for more details.
Incompatible Changes
Incompatible changes to classes are those changes for which the guarantee of interoperability cannot be maintained. The incompatible changes that may occur while evolving a class are:
Deleting fields
Moving classes up or down the hierarchy
Changing a nonstatic field to static or a nontransient field to transient
Changing the declared type of a primitive field
Changing the writeObject or readObject method so that it no longer writes or reads the default field data or changing it so that it attempts to write it or read it when the previous version did not.
Changing a class from Serializable to Externalizable or vice versa.
Changing a class from a non-enum type to an enum type or vice versa.
Removing either Serializable or Externalizable.
Adding the writeReplace or readResolve method to a class, if the behavior would produce an object that is incompatible with any older version of the class.
Link from where the above information is taken
http://docs.oracle.com/javase/7/docs/platform/serialization/spec/version.html#6678

Categories

Resources