Remove xsi:type, xmlns:xs, and xmlns:xsi from JAXB Generics

18,273

Solution 1

Test Generic

Your JAXB (JSR-222) implementation is going to create mappings per class (i.e. TestGeneric, not type (i.e. TestGeneric<Integer>). As such it is going to treat the value field as type Object. This will result in the xsi:type attributes as your JAXB implementation is adding enough information to be able to unmarshal the same types.

@XmlAccessorType(XmlAccessType.NONE)
static class TestGeneric<T> {
    @XmlAttribute public boolean isRequired;
    @XmlElement public T value;

    public TestGeneric() {
    }

    public TestGeneric(boolean isRequired) {
        this.isRequired = isRequired;
    }
}

Demo

Below is an approach you could use. I have introduced subclasses of TestGeneric to represent the different possible types.

package forum11192623;

import java.io.PrintWriter;
import java.io.StringWriter;

import javax.xml.bind.*;
import javax.xml.bind.annotation.*;

public class Demo {

    public static void main(String[] args) {
        try {
            TestRoot root = new TestRoot();
            root.name.value = "bobby";
            root.age.value = 102;
            root.color.value = "blue";

            JAXBContext context = JAXBContext.newInstance(root.getClass());
            Marshaller marsh = context.createMarshaller();
            marsh.setProperty(Marshaller.JAXB_ENCODING,"UTF-8");
            marsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);

            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            marsh.marshal(root,pw);
            System.out.println(sw.toString());
        }
        catch(Throwable t) {
            t.printStackTrace();
        }
    }

    @XmlRootElement
    static class TestRoot {
        @XmlElement public TestString name = new TestString(true);
        @XmlElement public TestInteger age = new TestInteger(true);
        @XmlElement public TestString color = new TestString(true);
    }

    @XmlAccessorType(XmlAccessType.NONE)
    @XmlTransient
    @XmlSeeAlso({TestInteger.class, TestString.class})
    static class TestGeneric<T> {
        @XmlAttribute 
        public boolean isRequired;
        public T value;

        public TestGeneric() {
        }

        public TestGeneric(boolean isRequired) {
            this.isRequired = isRequired;
        }
    }

    static class TestInteger extends TestGeneric<Integer> {
        public TestInteger() {
        }
        public TestInteger(boolean b) {
            super(b);
        }
        @XmlElement
        public Integer getValue() {
            return value;
        }
        public void setValue(Integer value) {
            this.value = value;
        }
    }

    static class TestString extends TestGeneric<String> {
        public TestString() {
        }
        public TestString(boolean b) {
            super(b);
        }
        @XmlElement
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
    }

}

Solution 2

I was able to use @XmlAnyElement(lax=true) on the property that was causing me problems due to the generic parameter, then had to add an @XmlRootElement on the actual class that was being marshalled, and the declaration went away! Fantastic!

Solution 3

i get the same problem, and resolved it by the advice from Programmatically determine if a JAXB annotation will result in a xsi:type annotation?

just using @XmlAnyElement(lax=true) instead of using @XmlElement on the generic class object (value for this sample).

Share:
18,273
esotericpig
Author by

esotericpig

beautiful code, beautiful life

Updated on June 04, 2022

Comments

  • esotericpig
    esotericpig almost 2 years

    When using JAXB, I'd like to remove the excess namespaces/types from my XML elements when using Generics. How can I do this or what am I doing wrong? I'd like to use Generics so that I only have to write a block of code once.

    Example code:

    public static void main(String[] args) {
        try {
            TestRoot root = new TestRoot();
            root.name.value = "bobby";
            root.age.value = 102;
            root.color.value = "blue";
    
            JAXBContext context = JAXBContext.newInstance(root.getClass());
            Marshaller marsh = context.createMarshaller();
            marsh.setProperty(Marshaller.JAXB_ENCODING,"UTF-8");
            marsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
    
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            marsh.marshal(root,pw);
            System.out.println(sw.toString());
        }
        catch(Throwable t) {
            t.printStackTrace();
        }
    }
    
    @XmlRootElement
    static class TestRoot {
        @XmlElement public TestGeneric<String> name = new TestGeneric<String>(true);
        @XmlElement public TestGeneric<Integer> age = new TestGeneric<Integer>(true);
        @XmlElement public TestWhatIWouldLike color = new TestWhatIWouldLike(true);
    }
    
    @XmlAccessorType(XmlAccessType.NONE)
    static class TestGeneric<T> {
        @XmlAttribute public boolean isRequired;
        @XmlElement public T value;
    
        public TestGeneric() {
        }
    
        public TestGeneric(boolean isRequired) {
            this.isRequired = isRequired;
        }
    }
    
    @XmlAccessorType(XmlAccessType.NONE)
    static class TestWhatIWouldLike {
        @XmlAttribute public boolean isRequired;
        @XmlElement public String value;
    
        public TestWhatIWouldLike() {
        }
    
        public TestWhatIWouldLike(boolean isRequired) {
            this.isRequired = isRequired;
        }
    }
    

    Output:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <testRoot>
        <name isRequired="true">
            <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">bobby</value>
        </name>
        <age isRequired="true">
            <value xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">102</value>
        </age>
        <color isRequired="true">
            <value>blue</value>
        </color>
    </testRoot>
    
  • esotericpig
    esotericpig almost 12 years
    This will work, but I feel like it defeats the purpose of Generics -- which for me is to write & maintain one block of code that will be useable for any Object without having to make new classes. I guess I will have to just deal with having the types specified, unless someone else has any other good ideas?