@XmlElements marked with @XmlJavaTypeAdapters?
Asked Answered
P

2

7

I have this situation

@XmlType(name ="", propOrder={"value"})
@XmlRootElement(name = "compound")
public class Compound extends Value {
  @XmlElements({
  @XmlElement(name="simple", type=Simple.class),
  @XmlElement(name="compound", type=Compound.class)
  })
  protected List<Value> value;
  // ...
}

So a Compound is a List of both Simple and/or Compound. Both extend from Value that is defined as

public abstract class Value implements Serializable {}

Simple is a class marked with an adapter to marshal/unmarshal to/from a simple string

@XmlJavaTypeAdapter(SimpleAdapter.class)
public class Simple extends Value {
  private java.lang.String simple;
  // ...
}

Compound does not need an adapter.

The problem is that if I use a Simple 'as is', it correctly marshals/unmarshals as

<simple>my.text.here</simple>

but if I use it inside a Compound it outputs something like

<compound>
  //...
  <simple>
    <value>my.text.here</value>
  </simple>
  //...
</compound>

And I'm just wondering why... Do I miss something? How can i remove that 'value'? It seems to me that the Adapter is not used at all, is it possible to use adapters in types marked inside @XmlElements?

EDIT

After few tests i found that the problem could be in how i handle a Simple instance. So I simplify my initial question in:

Given the a Simple class like

@XmlRootElement("simple")
public class Simple {
  private java.lang.String innerText;
  // getters/setters
}

how can i obtain a marshalled output like

<simple>
  my.inner.text.here
</simple>

instead of

<simple>
  <value>my.inner.text.here</value>
</simple>

?

Peasant answered 21/5, 2011 at 22:40 Comment(6)
A minor thing. You wrote both Compound and Simple inherit from Value, but that isn't reflected in their definitions.Bigamist
Another thing. Are you sure the output if marshalled within Compound is <simple><value>my.text.here</value></simple> and not <simple><simple>my.text.here</simple></simple>? I would understand the latter, but I don't see why would the former happen. Where's the value element name defined?Bigamist
All in all, it looks like you have far bigger model and you are manually extracting the most key things and posting it here. I am not sure if you run them. Maybe it would be great if you extracted a simple example (as you try), run it and share the whole context and outcome with us?Bigamist
You are right, this is just an extract of a bigger generated (and much edited) schema that i'm not totally allowed to post. Anyway i'll try to make a simple testcase to show what i mean. I've edited the sample to fix inheritance, and no i don't know where that 'value' comes from.Peasant
The reason I posted these comments is tried something what I thought follows your description, but my results were different. And as usual these are the details which matter. I totally understand posting the whole model might be out of question, but even it was possible it wouldn't constitute a good example.Bigamist
After few tests i simplified the question.Peasant
S
2

It sounds like you want private java.lang.String innerText; to be the @XmlValue of your Simple class. Try to annotate the String in Simple with the @XmlValue tag:

@XmlRootElement("simple")
public class Simple {
  @XmlValue
  private java.lang.String innerText;
  //getters/setters
}

Or if you were using annotations on your getter method (which I assume based on your XML output in the question change your @XmlElement tag to a @XmlValue tag:

@XmlValue
public java.lang.String getInnerText() {
  return innerText;
}

When I do this I get the output you are looking for in your edited question.

Sola answered 23/5, 2011 at 21:47 Comment(3)
Thank you, now the problem is another one... Simple class extends an abstract base class called Value. When i try to marshall it, i get something like "The property or field s can not be annotated with XmlValue since it is a subclass of another class". How con i solve this?Peasant
Ok, add @XmlTransient to the Value class. That should solve the problem.Sola
@ciosbel did you try the @XmlTransient annotation on the Value class? It gave me the result you were looking for with the implementation I provided above.Sola
W
0

The answer given by bamana is correct, however the exception you are seeing is due to a bug in the JAXB reference implementation. This bug also existed in EclipseLink JAXB (MOXy) but has been fixed in the 2.3.0 stream, a nighty download can be obtained here:

As a workaround you could use the XmlAdapter approach that was in your original question:

SimpleAdapter

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

public class SimpleAdapter extends XmlAdapter<String, Simple> {

    @Override
    public Simple unmarshal(String v) throws Exception {
        Simple simple = new Simple();
        simple.setSimple(v);
        return simple;
    }

    @Override
    public String marshal(Simple v) throws Exception {
        return v.getSimple();
    }

}

Simple

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

@XmlJavaTypeAdapter(SimpleAdapter.class)
public class Simple extends Value {
    private java.lang.String simple;

    public java.lang.String getSimple() {
        return simple;
    }

    public void setSimple(java.lang.String simple) {
        this.simple = simple;
    }

}

Compound

import java.util.List;

import javax.xml.bind.annotation.*;

@XmlRootElement(name = "compound")
@XmlAccessorType(XmlAccessType.FIELD)
public class Compound extends Value {
    @XmlElements({ @XmlElement(name = "simple", type = Simple.class),
            @XmlElement(name = "compound", type = Compound.class) })
    protected List<Value> value;

    public List<Value> getValue() {
        return value;
    }

    public void setValue(List<Value> value) {
        this.value = value;
    }

}

Value

import java.io.Serializable;

public abstract class Value implements Serializable {}

Demo

import java.io.File;

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

public class Demo {

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

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Compound compound = (Compound) unmarshaller.unmarshal(new File("input.xml"));
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(compound, System.out);
    }

}

input.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<compound>
    <simple>
        <simple>FOO</simple>
    </simple>
    <compound/>
</compound>
Wyon answered 24/5, 2011 at 13:37 Comment(4)
That was exactly my original idea, but i don't want a simple to be marshalled like your input file, without that inner <simple> wrapper.Peasant
@ciosbel - I believe the behaviour you are seeing is a bug, I am currently trying to fix this behaviour in EclipseLink JAXB (MOXy). However if you do use MOXy you can just use the @XmlValue without the @XmlJavaTypeAdapter anyways.Wyon
@ciosbel - You can track the progress of this issue in EclipseLink JAXB (MOXy) via the following bug: bugs.eclipse.org/347026Wyon
So this is a bug. Thank you. If it's painless I'll try to switch to the MOXy implementation and test the @XmlValue hint.Peasant

© 2022 - 2024 — McMap. All rights reserved.