开发者

Jaxb: xs:attribute null values

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 googli开发者_JAVA技巧ng 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.


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>


Starting from Java Objects

For fields/properties mapped as @XmlAttribute, a JAXB implementation (Metro, MOXy, JaxMe, etc) will marshal an empty String ("") value as property="". You can use an XmlAdapter to expose your null values as empty Strings to get the desired behaviour:

NullStringAdapter

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

public class NullStringAdapter extends XmlAdapter<String, String> {

    @Override
    public String unmarshal(String v) throws Exception {
        if("".equals(v)) {
            return null;
        }
        return v;
    }

    @Override
    public String marshal(String v) throws Exception {
        if(null == v) {
            return "";
        }
        return v;
    }

}

Root

The following is how you specify the adapter in your domain model. The same adapter can be used on many properties:

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class Root {

    private String foo;
    private String bar;

    @XmlAttribute
    @XmlJavaTypeAdapter(NullStringAdapter.class)
    public String getFoo() {
        return foo;
    }

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

    @XmlAttribute
    @XmlJavaTypeAdapter(NullStringAdapter.class)
    public String getBar() {
        return bar;
    }

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

}

Demo

You can demonstrate the concept, by running the following demo code:

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

The following is the demo code output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root bar="" foo=""/>

For More Information on JAXB's XmlAdapter See:

  • http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
  • http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
  • http://bdoughan.blogspot.com/2010/12/represent-string-values-as-element.html


You may use default values plugin for that.

Please look that question: JAXB xjc: How to generate code for Strings that returns empty if the value is null?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜