Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jaxb: xs:attribute null values

Tags:

jaxb

Reg: Jaxb

I'm basically trying to set up a role in JAXB which says that whenever an null field is encountered, instead of ignoring it in the output, set it to an empty value.

For xmlElement I got answer like we need to use nillable="true" but for how we need to set the null value. by googling I found that we need to use use="optional" but its not working in my case.

My xsd's part is below:

 <xs:attribute name="RomVersion" type="xs:string" use="required" /> 
 <xs:attribute name="MACAddress" type="xs:string" use="required" /> 
 <xs:attribute name="LargestFreeBlock" type="xs:unsignedInt" use="required" /> 
 <xs:attribute name="TimeSinceLastReset" type="xs:unsignedInt" use="optional" /> 
 <xs:attribute name="ResetReason" type="xs:string" use="optional" /> 
 <xs:attribute name="TimeStamp" type="xs:unsignedInt" use="optional" /> 
 <xs:attribute name="ECOList" type="xs:string" use="optional" /> 
 </xs:complexType>
 </xs:element>

Please give me the solution ASAP if anyone knows.

like image 406
Rekha Avatar asked May 13 '11 09:05

Rekha


1 Answers

Starting from XML Schema

In a previous answer I described how to solve your use case when starting from Java objects. Based on your comments to that answer, this answer describes how the same thing can be done when the model is generated from an XML schema.

XML Schema (attributeAdapter.xsd)

For this example we will use the following XML schema:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema 
    elementFormDefault="qualified"
    targetNamespace="http://www.example.com/adapter" 
    xmlns:nytd="http://www.example.com/adapter" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:element name="root">
        <xs:complexType>
            <xs:attribute name="foo" type="xs:string"/>
            <xs:attribute name="bar" type="xs:string"/>
        </xs:complexType>
    </xs:element>

</xs:schema>

StringConverter

We will need to define a class to do our special String handling. For this use case we want a null field/property value to be treated as empty String ("") in the XML document:

package com.example.adapter;

public class StringConverter {

    public static String parseString(String value) {
        if("".equals(value)) {
            return null;
        }
        return value;
    }

    public static String printString(String value) {
        if(null == value) {
            return "";
        }
        return value;
    }

}

Binding File (attributeAdapterBinding.xml)

We will need to use a JAXB binding file to customize the class generation. The binding file below will allow us to leverage the StringConverter class that we defined above:

<jaxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">
    <jaxb:bindings schemaLocation="attributeAdapter.xsd">
        <jaxb:bindings node="//xs:element[@name='root']/xs:complexType">
            <jaxb:bindings node="xs:attribute[@name='foo']">
                <jaxb:property>
                    <jaxb:baseType>
                        <jaxb:javaType name="java.lang.String"
                            parseMethod="com.example.adapter.StringConverter.parseString"
                            printMethod="com.example.adapter.StringConverter.printString"/>
                    </jaxb:baseType>
                </jaxb:property>
            </jaxb:bindings>
            <jaxb:bindings node="xs:attribute[@name='bar']">
                <jaxb:property>
                    <jaxb:baseType>
                        <jaxb:javaType name="java.lang.String"
                            parseMethod="com.example.adapter.StringConverter.parseString"
                            printMethod="com.example.adapter.StringConverter.printString"/>
                    </jaxb:baseType>
                </jaxb:property>
            </jaxb:bindings>
        </jaxb:bindings>
    </jaxb:bindings>
</jaxb:bindings>

XJC call

We will make our XJC call as follows:

xjc -d out -b attributeAdapterBinding.xml attributeAdapter.xsd

Domain Model (Root)

The fields/properties that we customized in the binding file will be annotated with @XmlJavaTypeAdapter;

package com.example.adapter;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "root")
public class Root {

    @XmlAttribute
    @XmlJavaTypeAdapter(Adapter1 .class)
    protected String foo;

    @XmlAttribute
    @XmlJavaTypeAdapter(Adapter2 .class)
    protected String bar;

    public String getFoo() {
        return foo;
    }

    public void setFoo(String value) {
        this.foo = value;
    }

    public String getBar() {
        return bar;
    }

    public void setBar(String value) {
        this.bar = value;
    }

}

XmlAdapter (Adapter1)

The generated XmlAdapter class will look something like the following. Note how it leverages our StringConverter class:

package com.example.adapter;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class Adapter1 extends XmlAdapter<String, String> {

    public String unmarshal(String value) {
        return (com.example.adapter.StringConverter.parseString(value));
    }

    public String marshal(String value) {
        return (com.example.adapter.StringConverter.printString(value));
    }

}

Demo

Now if we run the following demo code:

package com.example.adapter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Root root = new Root();
        root.setFoo(null);
        root.setBar(null);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }

}

Output

We will get the desired output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns="http://www.example.com/adapter" foo="" bar=""/>

UPDATE (Alternate Binding File)

Alternatively, if you wanted the adapter applied to all properties of type xsd:string then you could use an binding file that looked something like;

<jaxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">
    <jaxb:globalBindings>
        <jaxb:javaType 
            name="String"
            xmlType="xs:string"
            parseMethod="com.example.adapter.StringConverter.parseString"
            printMethod="com.example.adapter.StringConverter.printString"/>
    </jaxb:globalBindings>

</jaxb:bindings>
like image 176
bdoughan Avatar answered Sep 21 '22 00:09

bdoughan