Given:
#XmlRootElement(name = "foo")
class Foo {
public Bar getBar() {...}
}
class Bar {
#XmlElement(name = "string")
public String getString() {return "hello";}
}
How do I annotate so the XML will be:
<foo>
<string>hello</string>
</foo>
You could do the following leveraging the #XmlValue annotation.
Foo
#XmlRootElement
class Foo {
#XmlElement(name="string")
public Bar getBar() {...}
}
Bar
class Bar {
#XmlValue
public String getString() {return "hello";}
}
For More Information
http://blog.bdoughan.com/2011/06/jaxb-and-complex-types-with-simple.html
You probably need to use #XmlSeeAlso annotation on top of your class.
You can use #XmlSeeAlso annotation when you want another Entity bean to be included in the XML output. Can you try this in your Foo class
#XmlRootElement(name = "foo")
#XmlSeeAlso(Bar.class)
class Foo {
public Bar getBar() {...}
}
Update1:
For your comment to remove the bar tag in the XML try using EclipseLink JAXB (MOXy)'s. #XmlPath will solve your issue.
#XmlRootElement(name = "foo")
#XmlSeeAlso(Bar.class)
class Foo {
#XmlPath(".")
public Bar getBar() {...}
}
Refer here for more details.
I'm not sure you can to eliminate the tag bar from resulting XML:
http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html#wp148576
Related
I'd like to serialize/deserialize (json) a class that contains an attribute that is an interface, but the underlying class doesn't have any attributes. The below is my most simplified case and my best attempt at what to do.
This throws an error when trying to deserialize No suitable constructor found for type [simple type, class com.example.Bar]: can not instantiate from JSON object (need to add/enable type information?) at [Source: java.io.StringReader#301ec38b; line: 1, column: 2]
public interface FooInterface {
String doThing();
}
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
#EqualsAndHashCode
public class Foo implements FooInterface {
#Override
public String doThing() {
return "foo";
}
}
#Getter
#Setter
#EqualsAndHashCode
public class Bar {
FooInterface foo;
public Bar(FooInterface foo) {
this.foo = foo;
}
}
#Test
public void fooTest() throws IOException {
Foo foo = new Foo();
Bar bar = new Bar(foo);
String serialized = new ObjectMapper().writeValueAsString(bar); // = {"foo":{}}
Bar deserialized = new ObjectMapper().readValue(serialized, Bar.class);
Assert.assertEquals(bar, deserialized);
}
Please add default constructor to class Bar and I guess your issue should be resolved.
#Getter
#Setter
#EqualsAndHashCode
public class Bar {
FooInterface foo;
public Bar() {}
public Bar(FooInterface foo) {
this.foo = foo;
}
}
Do let me know if this doesn't solve your problem, I will try to dig deeper.
As #Aditya mentioned I was missing the default constructor which was causing the error I was having, but then the new error led me to finding this question which was the crux of the problem that this question was asking about.
Looks like I misunderstood what the JsonAutoDetect annotation did. Below is the code that ended up working for me.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = Foo.class),
})
public interface FooInterface {
String doThing();
}
#EqualsAndHashCode
public class Foo implements FooInterface {
#Override
public String doThing() {
return "foo";
}
}
#Getter
#Setter
#EqualsAndHashCode
public class Bar {
FooInterface foo;
public Bar() {}
public Bar(FooInterface foo) {
this.foo = foo;
}
}
#Test
public void fooTest() throws IOException {
Foo foo = new Foo();
Bar bar = new Bar(foo);
String serialized = new ObjectMapper().writeValueAsString(bar); // {"foo":{"type":"Foo"}}
Bar deserialized = new ObjectMapper().readValue(serialized, Bar.class);
Assert.assertEquals(bar, deserialized);
}
Is there a way with jaxb to create a custom element from two fields from an object?
Here's an example of what I have and what I want. I realize I could extract the fields into a seperate annotated class, but I'm curious of theres a way to do something similar to this.
#XmlRootElement()
public class Foo {
public String bar
public String baz
}
Expected output xml:
<foo>
<customElement bar="barValue">bazValue</customElement>
</foo>
Thanks!
class for foo
#XmlRootElement()
public class Foo {
private customElement CustomElement;
public CustomElement getCustomElement(){
return customElement;
}
#XmlElement
public void setCustomElement(CustomElement customElement){
this.customElement = customElement;
}
}
class for custom element
#XmlAccessorType(XmlAccessType.FIELD)
public class CustomElement {
#XmlAttribute
private String bar;
#XmlValue
private String baz
// set getters and setters
}
We are using MOXy JAXB in our project.
Model class:
#XmlRootElement(name = "field")
#XmlType(propOrder = {"id","value"})
public class FieldData{
#XmlAttribute
private String id;
#XmlAttribute
private Object value;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
For my use case, I want value to be of type Object itself as I may get any primitive data type value here. I will take them as strings initially. Once I get the object, I do the type conversion and save it into the same field. The above use case is working fine. But when I change #XmlAttribute to #XmlElement it is not working. I see that the value is unmarshalled as an instance of ElementNSImpl. Is there any work around for this?
Here is a brain dump on what you are seeing:
Demo Code
I will use the same demo code with the different mappings described below:
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
Foo foo = (Foo) unmarshaller.unmarshal(xml);
System.out.println(foo.getBar().getClass());
}
}
Use Case #1 - Object Property Mapped With #XmlAttribute
Java Model
Foo
We use the #XmlAttribute annotation to map a property to an #XmlAttribute. Note: This isn't a valid configuration when using the JAXB reference implementation.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Foo {
private Object bar;
#XmlAttribute
public Object getBar() {
return bar;
}
public void setBar(Object bar) {
this.bar = bar;
}
}
XML #1
input.xml
In the XML document below the bar attribute contains numeric digits.
<?xml version="1.0" encoding="UTF-8"?>
<foo bar="123"/>
output
As there is no typing information (in the XML or in the Java class), MOXy brings the value in as a String. A String is the most concrete type that can represent all possible values on an XML attribute.
class java.lang.String
XML #2
input.xml
In the XML document below the bar attribute contains alphabet characters.
<?xml version="1.0" encoding="UTF-8"?>
<foo bar="Hello World"/>
output
A String is the most concrete type that can represent all possible values on an XML attribute.
class java.lang.String
Use Case #2 Object Property Mapped With #XmlElement
Java Model
Foo
In this version of the Foo class we will not annotate the bar property, this is the same as annotating it with #XmlElement.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Foo {
private Object bar;
public Object getBar() {
return bar;
}
public void setBar(Object bar) {
this.bar = bar;
}
}
XML #1 (Simple Element)
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>Hello World</bar>
</foo>
Output
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
XML #2 (Complex Element)
input.xml
Instead of containing just test, now the bar element contains XML attributes and child elements.
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar a="1">
<b>2</b>
<c>3</c>
</bar>
</foo>
Output
Now we start to see why JAXB treats the value as a DOM, the element could be arbitrarily complex so a DOM element becomes a structure that can hold any possible value.
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
XML #3 (Typed Element)
input.xml
In the XML document below
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">Hello World</bar>
</foo>
Output
class java.lang.String
Use Case #3 Object Property Mapped With #XmlElement(type=String)
Java Model
Foo
In this version of the Foo class we will annotate the bar property with #XmlElement(type=String.class). As far as Java is concerned the property is still of typeObject, but JAXB will treat the property as if it's typeString`.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Foo {
private Object bar;
#XmlElement(type=String)
public Object getBar() {
return bar;
}
public void setBar(Object bar) {
this.bar = bar;
}
}
XML #1 (Simple Element)
Now we see that the value of the bar element is treated as a String.
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>Hello World</bar>
</foo>
Output
class java.lang.String
I'm using JAXB to unmarshal some xml into an object(s).
I have a class which inherit from an abstract class. I've marked the abstract class as #XmlTransient. Then using XMLType PropOrder I can access the properties in the abstract class like so:
#XmlType( propOrder = { "id"...
Cool. Problem is sometimes it isn't an element that I want to access but rather an attribute. Normally you would define such a property using #XMLAttribute to indicate the value is stored in an xml attribute and not an element. But given the fact that I've already used XMLTransient on the abstract class where 'id' is defined, JAXB complains when I try to mark the field as #XMLAttribute.
JAXB is complaining that I'm trying to access/return two fields of with the same name.
Can anyone please point me in the right direction? I'm building for GAE so I dn't really want to use any other libraries.
Thanks in advance!
Below are a couple of things you can do:
Java Model
Foo
You can annotate the property on the parent class with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlTransient
public class Foo {
private String att1;
private String att2;
#XmlAttribute
public String getAtt1() {
return att1;
}
public void setAtt1(String att1) {
this.att1 = att1;
}
public String getAtt2() {
return att2;
}
public void setAtt2(String att2) {
this.att2 = att2;
}
}
Bar
You can override the property on the subclass and annotate it with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Bar extends Foo {
#Override
#XmlAttribute
public String getAtt2() {
return super.getAtt2();
}
#Override
public void setAtt2(String att2) {
super.setAtt2(att2);
}
}
Demo Code
Demo
Here is some demo code you can run to show that everything works.
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Bar.class);
Bar bar = new Bar();
bar.setAtt1("a");
bar.setAtt2("b");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(bar, System.out);
}
}
Output
Below is the output from running the demo code:
<?xml version="1.0" encoding="UTF-8"?>
<bar att1="a" att2="b"/>
This is what I'm trying to do:
#XmlRootElement(name = "bar")
#XmlAccessorType(XmlAccessType.NONE)
public abstract class Bar {
}
public final class Foo extends Bar {
#XmlElement
public String getMsg() {
return "hello, world!";
}
}
Now I'm trying to marshall an instance of class Foo:
com.sun.istack.SAXException2: unable to marshal type "Foo" as
an element because it is missing an #XmlRootElement annotation
What is a workaround?
Can you get away without the #XmlRootElement annotation on the superclass. Instead you should put it on each of your subclasses. JAX-B will still know about the superclass fields.