GuiCavi
GuiCavi

Reputation: 53

JAXB Element without attribute

I'm working with JAXB and need to generate a XML code like this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?><!--Created with JFLAP 6.4.-->
<structure>
    <type>fa</type>
    <automaton>
        <!--The list of states.-->
        <state id="0" name="q0">
            <x>160.0</x>
            <y>151.0</y
            <initial/>                 <!-- This what I want-->
        </state>
        <state id="1" name="q1">
            <x>369.0</x>
            <y>94.0</y>
            <final/>                   <!-- This what I want-->
        </state>
        <!--The list of transitions.-->
        <transition>
            <from>0</from>
            <to>1</to>
            <read>a</read>
        </transition>
    </automaton>
</structure>

As you can see, I wanna know how to create a simple @XmlElement without @XmlAttribute, but in my code, I got:

private boolean initial = false;
private boolean final   = false;

@XmlElement(name="initial")
public void setInitial(boolean val) {
    this.initial = val;
}

@XmlElement(name="final")
public void setFinal(boolean val) {
    this.final = val;
}

This way, I got a XML like this:

<state id="0" name="q0">
        <x>0.0</x>
        <y>0.0</y>
        <final>false</final>
        <initial>true</initial>
</state>
<state id="1" name="q1">
        <x>0.0</x>
        <y>0.0</y>
        <final>true</final>
        <initial>false</initial>
</state>

Does anyone knows how to do it?

Upvotes: 3

Views: 1844

Answers (2)

A4L
A4L

Reputation: 17595

You could fake an empty node by setting an empty string, for this you need to turn your booleans to Strings.

@XmlRootElement
public static  class Structure {
    @XmlElement String type;
    @XmlElement Automaton automaton;
    public Structure() { automaton = new Automaton(); }
    Structure(String t) { this(); type = t; }
}

public static class Automaton {
    @XmlElement List<State> state;
    public Automaton() { state = new ArrayList<>(); }
    State addState(State s) {state.add(s); return s;};
}

public static class State {
    @XmlAttribute String id, name;
    @XmlElement double x, y;
    @XmlElement String initial;
    @XmlElement(name="final") String final_;
    public State() {};
    State(String id, String n, double x, double y)
    {this.id = id; name = n; this.x = x; this.y = y;};
}

Create some data and marshal it:

@Test
public void jaxbEmptyEmlements() throws JAXBException {
    JAXBContext c = JAXBContext.newInstance(Structure.class);
    Marshaller m = c.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
    Structure o = new Structure("fa");
    o.automaton.addState(new State("0", "q0", 160d, 151d)).initial = "";
    o.automaton.addState(new State("1", "q1", 369d, 94d)).final_ = "";
    m.marshal(o, System.out);
}

This will output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<structure>
    <type>fa</type>
    <automaton>
        <state id="0" name="q0">
            <x>160.0</x>
            <y>151.0</y>
            <initial></initial>
        </state>
        <state id="1" name="q1">
            <x>369.0</x>
            <y>94.0</y>
            <final></final>
        </state>
    </automaton>
</structure>

Note that <initial></initial> is the same as <initial/>, same for final.

Alternatively you could rethink the node for the state type and make an element <state-type> and set it with the value an enum, for example enum StateType { INITIAL, FINAL} or simply set it to the strings initial or final.

EDIT enter image description here

Upvotes: 0

bdoughan
bdoughan

Reputation: 148977

You could do the following and leverage an XmlAdapter to convert a Boolean to an empty object to get the desired XML representation.

Java Model

Root

The property is boolean, but we will make the field Boolean so we can apply an XmlAdapter to it. We will also specify that we want JAXB to map to the field (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    @XmlJavaTypeAdapter(BooleanAdapter.class)
    private Boolean foo = false;

    @XmlJavaTypeAdapter(BooleanAdapter.class)
    private Boolean bar = false;

    public boolean isFoo() {
        return foo;
    }

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

    public boolean isBar() {
        return bar;
    }

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

}

BooleanAdapter

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

public class BooleanAdapter extends XmlAdapter<BooleanAdapter.AdaptedBoolean, Boolean> {

    public static class AdaptedBoolean {
    }

    @Override
    public Boolean unmarshal(AdaptedBoolean v) throws Exception {
        return null != v;
    }

    @Override
    public AdaptedBoolean marshal(Boolean v) throws Exception {
        if(v) {
            return new AdaptedBoolean();
        } else {
            return null;
        }
    }

}

Demo Code

Below is some demo code that you can run to see that everything works.

Demo

import javax.xml.bind.*;

public class Demo {

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

        Root foo = new Root();
        foo.setFoo(true);
        foo.setBar(false);

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

}

Output

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

Upvotes: 1

Related Questions