Search code examples
javaspringxsdxmlbeans

XSD date types with minOccurs set to 0 now has a value of "<xml-fragment..." (nillable defaults to false)


Forgive me in advance if I don't provide enough information, this is an issue I am seeing and tasked with fixing but I did not personally write this code originally (the person who did has moved on).

We are using Apache XMLBeans to generate some Java classes from a set of XSDs. Our web application then generates a web HTML form to create instances of these classes (or modify fields of existing classes). To put this into some context; one of our XSDs represents a person. We can then use our generic form generation code to generate an HTML form for a user to supply information about a particular person. There a number of date attributes associated with a person, e.g. date of birth.

I have made some substantial changes recently to the project's structure and dependencies, but nothing directly related to the code the generates these Java classes and HTML form from them. Before these changes were made everything worked, now, all of the date fields have a value of <xml-fragment uid="theAttributesId"/> if they have not had any value set. In the XSDs, these date attributes/elements have the minOccurs attribute set to 0 (and hence should be optional). If I set the attribute nillable to true for each one the probably no longer exists.

Having spent some time debugging I can see that an empty date value is not a valid value unless nillable is true. In the XMLBeans class XmlObjectBase the validate method returns false for null date fields. What I don't understand is what has changed and why is there now a need for the nillable attribute. Dependencies have changed but having compared XMLBean versions they all seem the same:

We have three jars in our web app's lib directory that contain the class XmlObjectBase; tika-app-0.7, xbeans-2.2.0 and xmlBeans-2.3.0. All versions are identical to the lib directory before the massive restructure. The XSD files haven't changed. What has?

I have found the following links which helped in my investigations so far:

I have accepted that maybe the only realistic way of solving this issue without vasts amount of time is to add the nillable attribute. However, I am not keen to add it for all date elements in the XSDs. Fortunately I have 3 date base types defined that all date elements are. I have tried modifying the base types' default nillable attribute to true without success:

Original NonFutureDateBaseType:

<xs:complexType name="nonFutureDateType">
    <xs:simpleContent>
        <xs:extension base="cr:nonFutureDateBaseType">
            <xs:attribute name="uid" type="xs:int" />
        </xs:extension>
    </xs:simpleContent>
</xs:complexType>

(where cr:nonFutureDateBaseType is a simple type with a restriction of the standard XSD date type)

New with attempted nillable default as true:

<xs:complexType name="nonFutureDateType">
    <xs:simpleContent>
        <xs:extension base="cr:nonFutureDateBaseType">
            <xs:attribute name="uid" type="xs:int" />
    <xs:attribute name="nillable" type="xs:boolean" default="true" />
        </xs:extension>
    </xs:simpleContent>
</xs:complexType>

Anyone have any suggestions as to why the above doesn't work? Or what else I should look into?

Apologies for the lengthy question (and lack of formatting - I have no SO images/icons for some reason) and thanks in advance.

More Detail Edit

I've just been looking in the database at the XML that represents a person. I have something like this:

<PERSON>
    ...
    <BIRTH_DATE uid="12" 
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                xsi:nil="true" />
    ...
</PERSON>

And then the value of BIRTH_DATE evaluates to <xml-fragment uid="theAttributesId"/>. Wtf? From what I have read that should mean it evaluates to ''. Hmmm...

Another Edit:

I'm getting seriously fed up with this issue so have started investigating the differences between the latest web app and the one running the earlier codebase. The major difference I see when debugging is this:

New codebase: invoking java.beans.PropertyEditorSupport.getValue() returns <xml-fragment uid="12"/>

Old codebase: invoking java.beans.PropertyEditorSupport.getValue() returns <xml-fragment uid="12" xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>

Between my JSP that generates the form and the setter for the value, there are a lot of Spring classes. The key difference between the new codebase and the old (working) codebase is the Spring version.

The new codebase depends on spring-context-3.0.5.RELEASE, whereas the old depends on spring-context-2.5.6.SEC02.

Any suggestions would greatly appreciated!


Solution

  • Having done a significant amount of debugging, I have an answer - I now know what the difference between the two versions is:

    The Spring class org.springframework.web.servlet.tags.form.ValueFormatter has a slight difference between version 2.5.6.SEC02 and 3.0.5.RELEASE.

    The implementation of the method getDisplayString differs between the two versions:

    2.5.6.SEC02 (Working correctly in my application)

    if (propertyEditor != null && !(value instanceof String)) {
        try {
            propertyEditor.setValue(value);
            return getDisplayString(propertyEditor.getAsText(), htmlEscape);
        }
        catch (Throwable ex) {
            // The PropertyEditor might not support this value... pass through.
            return getDisplayString(value, htmlEscape);
        }
    }
    else {
        return getDisplayString(value, htmlEscape);
    }
    

    3.0.5.RELEASE (Display the XML fragment if no date value is set):

    if (propertyEditor != null && !(value instanceof String)) {
        try {
            propertyEditor.setValue(value);
            String text = propertyEditor.getAsText();
            if (text != null) {
                return getDisplayString(text, htmlEscape);
            }
        }
        catch (Throwable ex) {
            // The PropertyEditor might not support this value... pass through.
        }
    }
    return getDisplayString(value, htmlEscape);
    

    The text returned from propertyEditor.getAsText() always returns null for me when the date has no value and hence the 'value' property is used. I believe the issue is actually in some code in my application (the custom XmlBaseTypePropertyEditor is as fault it seems) and I pressume there is no issue with the Spring change.

    Sorry for wasting anyone's time. Thanks anyway.