How to serialize and de-serialize objects using JAXB? - java

I have an issue. I want to convert an object into another object using JAXB. As in, I have a class com.home.Student, and another class com.school.Student, both have same arguments, in fact both are same (copy paste), but different package. I want to perform the conversion between them using JAXB.
How to do that, please help me.

It would be nice if you included some code that explains your problem.
JAXB 101 says you should place the right annotations, then you can serialize and deserialize correctly. You should properly annotate your classes with #XmlRootElement, #XmlElement, #XmlAttribute, etc
For example:
#XmlRootElement(name="student")
#XmlAccessorType(XmlAccessType.NONE)
class Student {
#XmlElement(name="name")
private String name;
#XmlElement(name="age")
private int age;
public Student() {
}
public String getName() { return name; }
public int getAge() { return age; }
}
Then you can use serialize it using JAXB Marshaller:
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Student.class);
Marshaller m = context.createMarshaller();
m.marshal(student, writer);
And deserialize it as well by Unmarshelling the input ..
JAXBContext context = JAXBContext.newInstance(Student.class);
Unmarshaller m = context.createUnmarshaller();
return (Student)m.unmarshal(new StringReader(input));
Make sure you look at the JavaDoc I mentioned above since there are many ways to do so.
If you cannot modify your classes, you can still use JAXB (or you can use XStream) Assuming your class is the following:
class Student {
private String name;
private int age;
public Student() {
}
public void setName(String name) { this.name = name; }
public String getName() { return name; }
public void setAge(int age) { this.age = age; }
public int getAge() { return age; }
}
You can serialize it by doing:
Student student = new Student();
student.setAge(25);
student.setName('FooBar');
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Student.class);
Marshaller m = context.createMarshaller();
m.marshal(new JAXBElement(new QName(Student.class.getSimpleName()), Student.class, student), writer);
System.out.println(writer.toString());
If you are using XStream, you can do the serialization without Annotations too (and it is more controllable).
http://x-stream.github.io/tutorial.html

You could do the following.
Note:
It does not require that you ever materialize the data as XML, by leveraging JAXBSource.
It does not require any annotations on your object model.
com.home.Student
package com.home;
public class Student {
private String name;
private Status status;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
com.home.Status
package com.home;
public enum Status {
FULL_TIME("F"),
PART_TIME("P");
private final String code;
Status(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
com.school.Student
package com.school;
public class Student {
private String name;
private Status status;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
com.school.Status
package com.school;
public enum Status {
FULL_TIME("F"),
PART_TIME("P");
private final String code;
Status(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
com.example.Demo;
package com.example;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
com.home.Student studentA = new com.home.Student();
studentA.setName("Jane Doe");
studentA.setStatus(com.home.Status.FULL_TIME);
JAXBContext contextA = JAXBContext.newInstance(com.home.Student.class);
JAXBElement<com.home.Student> jaxbElementA = new JAXBElement(new QName("student"), com.home.Student.class, studentA);
JAXBSource sourceA = new JAXBSource(contextA, jaxbElementA);
JAXBContext contextB = JAXBContext.newInstance(com.school.Student.class);
Unmarshaller unmarshallerB = contextB.createUnmarshaller();
JAXBElement<com.school.Student> jaxbElementB = unmarshallerB.unmarshal(sourceA, com.school.Student.class);
com.school.Student studentB = jaxbElementB.getValue();
System.out.println(studentB.getName());
System.out.println(studentB.getStatus().getCode());
}
}

If your goal is simply to convert (assign actually) between the two, and they're identical except package name, I would think you could use simple reflection. Just iterate over the fields of source object, and assign to the field of the same name in the target object. Vaguely, like this:
import java.lang.reflect.Field;
public class Converter {
public void convert (com.home.Student src, com.school.Student dst) throws Exception {
for (Field f : src.getFields()) {
// src field name
String name = f.getName();
// get corresponding field in dst
Field dstField = dst.getDeclaredField(name);
dstField.set(dst, f.get());
}
}
}
Note: I didn't compile this, which is why I say "vaguely". You'll need to use the Field.isAccessible()/Field.setAccessible(true) if the fields are private, so that you can temporarily change accessibility while you're assigning values. Or, you an write slightly more complex code that uses public setters/getters rather than directly using field access.

Related

Can Jackson automatically treat any constructor parameter as a JsonProperty?

How do I get Jackson to treat 'name' as if it had a #JsonProperty annotation?
public class SimpleClass {
private String name;
private String doNotSerialize;
public SimpleClass( #JsonProperty("name") String name ) {
this.name = name;
}
public String getName() {
return name;
}
public int getSum() {
return 1+1;
}
}
The way it is now, I get an error, Unrecognized field "sum", because it treats every getter as a serializable property.
If I add a class annotation:
#JsonAutoDetect( getterVisibility = JsonAutoDetect.Visibility.NONE )
I get an empty string when serializing. I was hoping that Jackson would see the #JsonProperty on the constructor parameter and figure it out.
If I change the class annotation to:
#JsonAutoDetect( getterVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.ANY )
Then I get the 'doNotSerialize' field included.
If I set a #JsonCreator on the constructor, and change my autodetect, I still get a blank string:
#JsonAutoDetect( getterVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY )
public class SimpleClass {
private String name;
private String doNotSerialize;
#JsonCreator
public SimpleClass( #JsonProperty("name") String name ) {
this.name = name;
}
public String getName() {
return name;
}
public int getSum() {
return 1+1;
}
}
What I'm hoping is that somehow I can tell Jackson to treat all the constructor parameters as serializable fields, and all other fields / setters as non-serializable.
You can use a filter to only serialise getters which have a matching field, e.g.
package org.example;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import java.io.IOException;
import java.io.StringWriter;
public class App {
#JsonFilter("test")
public static class SimpleClass {
private String name;
private String doNotSerialize;
public SimpleClass(String name ) {
this.name = name;
}
public String getName() {
return name;
}
public int getSum() {
return 1+1;
}
}
public static void main( String[] args ) throws IOException {
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("test", new SimpleBeanPropertyFilter() {
#Override
protected boolean include(BeanPropertyWriter writer) {
return super.include(writer);
}
#Override
protected boolean include(PropertyWriter writer) {
String name = writer.getName();
Class clazz = writer.getMember().getDeclaringClass();
try {
clazz.getDeclaredField(name);
return super.include(writer);
} catch (NoSuchFieldException e) {
// ignore
return false;
}
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
StringWriter sw = new StringWriter();
mapper.createGenerator(sw).writeObject(new SimpleClass("foo"));
System.out.println(sw.toString());
}
}
I don't know your full requirements, but this should be a start.
I haven't tried to do what you actually, asked, that is, look at constructor parameters, but that should be possible too.
If you want "sum" to be included in the serializad json but want to ignore it when deserializing you can do:
#JsonIgnoreProperties(ignoreUnknown=true)
public class SimpleClass {
// properties/getters
public int getSum() { return 1+1; }
}
If you want to remove "sum" entirely from the json you can do
#JsonIgnoreProperties({"sum"})
public class SimpleClass {
// properties/getters
public int getSum() { return 1+1; }
}
or
public class SimpleClass {
// properties/getters
#JsonIgnore
public int getSum() { return 1+1; }
}

Decorate existing class to support javafx properties

I have an existing class, that I want to keep ignorant of javafx. Is there a generally accepted way to decorate or adapt it so that it supports javafx properties?
I want to do something like the following (which is obviously wrong):
public class FooWrapper {
/**
* Decorates a Foo instance with javafx stuff
*/
private final Foo foo;
public FooWrapper(Foo toWrap) {
this.foo = toWrap;
}
private final StringProperty name = new SimpleStringProperty();
public final StringProperty nameProperty() {
return this.foo.getName();//?????
}
public final String getName() {
return this.nameProperty().get();
}
public final void setFirstName(final String name) {
this.nameProperty().set(name);
}
}
public class Foo {
/**
* Basic class I want to keep ignorant of javafx
*/
private String name = "hello";
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Use the classes in the javafx.beans.property.adapter package.
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class FooAdapter {
private final JavaBeanStringProperty name;
public FooAdapter(Foo foo) {
try {
name = JavaBeanStringPropertyBuilder.create().bean(foo).name("name").build();
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
public final void setName(String name) {
this.name.set(name);
}
public final String getName() {
return name.get();
}
public final StringProperty nameProperty() {
return name;
}
}
The adapter property, as created above, requires that the underlying object follows the Java Bean convention for properties. However, there are ways to customize what methods to use for getters/setters.
The adapter property will get the value from the underlying object and, if writable, also write to the underlying object when updated. It can also observe the underlying object for changes if it supports PropertyChangeListeners. Note that this functionality is implemented using reflection; if you are using modules you need to add the appropriate exports/opens directives to your module-info (see the javadoc of the various properties, such as JavaBeanStringProperty, for details).

Reading xml document in java application

I've been doing a lot of solutions, but noone does it work. How can i print this XML code in java. The main problem is how can I print value xml:lang.
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/ofbiz-properties.xsd">
<property key="hour.plural">
<value xml:lang="ar">ساعات</value>
<value xml:lang="de">Stunden</value>
<value xml:lang="pl">oglądaj</value></property></resource>
It's the resource class
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="resource")
public class resource {
private String property;
private String value;
public root(String property, String value) {
this.property = property;
this.value = value;
}
public String getProperty() {
return property;
}
public String getValue() {
return value;
}
public String toString() {
return property + value;
}
}
And that's the main class
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import project1.resource;
public class xmlread {
public static void main(String... arg) throws Exception {
File file = new File("C:DateTimeLabels.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(resource.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
resource Resource = (resource) jaxbUnmarshaller.unmarshal(file);
System.out.println(Resource);
}
}
#XmlRootElement(name="resource")
public class resource {
private String property;
private String value;
public root(String property, String value) {
this.property = property;
this.value = value;
}
#XmlElement(name = "property")
public String getProperty() {
return property;
}
#XmlElement(name = "value")
public String getValue() {
return value;
}
public String toString() {
return property + value;
}
}`
Add this to your resource class, should help the marshaller find the exact properties you want.
You have more than one value, so for the sake of the <value> elements, you need to define a list of values.
The elements are contained inside the elements, so you have a nested structure here. That means you need a Property class contained in your Resource class, and your Property class need to contain a Value class.
You need to bind the key attribute and the xml:lang attributes.
Here is an example of Jaxb-annotated classes that does most of the work to map to your XML format. Complete as you need.
#XmlRootElement
public class Resource {
private Property property = new Property();
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
public static void main(String[] args) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(Resource.class);
Marshaller marshaller = context.createMarshaller();
Resource resource = new Resource();
resource.getProperty().setKey("hour.plural");
resource.getProperty().getValues().add(new Value("fr"));
resource.getProperty().getValues().add(new Value("it"));
resource.getProperty().getValues().add(new Value("ru"));
marshaller.marshal(resource, System.out);
}
}
class Property {
private String key;
private List<Value> values = new ArrayList<>();
#XmlAttribute
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
#XmlElement(name = "value")
public List<Value> getValues() {
return values;
}
public void setValues(List<Value> values) {
this.values = values;
}
}
class Value {
private String lang;
public Value() {
}
public Value(String lang) {
this.lang = lang;
}
#XmlAttribute
public String getLang() {
return lang;
}
public void setLang(String lang) {
this.lang = lang;
}
}

Deserialize an object's property which has an inconsistent name?

Using Retrofit here to consume Google Civic API.
The library requires you to create a model of what the API will return as I have done already with Election. Which is basically a copy of the google documentation.
(Retrofit binds the response properties to properties with the same name)
Election.Java :
public class Election {
private long id;
private String name;
private String electionDay;
private String ocdDivisionId;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getElectionDay() {
return electionDay;
}
public void setElectionDay(String electionDay) {
this.electionDay = electionDay;
}
public String getOcdDivisionId() {
return ocdDivisionId;
}
public void setOcdDivisionId(String ocdDivisionId) {
this.ocdDivisionId = ocdDivisionId;
}
}
But Representatives have an inconsistent property name, thus I don't see a way to model this in a way Retrofit will know how to deserialize the API's response.
Representatives object (JSON) :
property name is called (key)
How do I let Retrofit deserialize a model that captures the property named variable after a key of the division?
Assuming you're using a Gson converter, I personally would use a map. I guess the same can be achieved with other converters, but I never used them. Say you have the following object:
public class Division {
#SerializedName("name")
#Expose
private String name;
#SerializedName("alsoKnownAs")
#Expose
private List<String> alsoKnownAs = new ArrayList<>();
#SerializedName("officeIndices")
#Expose
private List<Integer> officeIndices = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getAlsoKnownAs() {
return alsoKnownAs;
}
public void setAlsoKnownAs(List<String> alsoKnownAs) {
this.alsoKnownAs = alsoKnownAs;
}
public List<Integer> getOfficeIndices() {
return officeIndices;
}
public void setOfficeIndices(List<Integer> officeIndices) {
this.officeIndices = officeIndices;
}
}
Which represents the object inside the divisions array. You can then have the class:
private class Divisions {
#SerializedName("divisions")
#Expose
private Map<String, Division> divisions = new HashMap<>();
// ...
}
Notice the usage of a map here? Behind the scenes Gson will be able to serialise and deserialise your objects. The class Divisions is the root of the json you gave us in the question.
Hope this helps

How to map the Mixins for the nested JSON response

I am using Jackson APIs for Mapping my JSON response into a java object.
For example,
for the response { name :'karthikeyan',age:'24',gender:'Male'}
#JsonProperty("name")
public String _name;
#JsonProperty("age")
public int _age;
#JsonProperty("gender")
public String _gender;
is the Mix-in and it works fine.(internally we will be mapping this pojo and Mix-in).Now how can i represent the following response in a Mix-in?
{
name :'karthikeyan',
age:'24',
gender:'Male',
interest:
{
books:'xxx',
music:'yyy',
movie:'zzz'
}
}
i have tried with the following, but no luck.
#JsonProperty("name")
public String _name;
#JsonProperty("age")
public int _age;
#JsonProperty("gender")
public String _gender;
#JsonProperty("interest")
public InterestPojo interestPojo; //created same format mix-in and pojo for interest params as well.
but unable to map them exactly, give your comments and thoughts on how to do it ?
I tried the following:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(new Something("Name", 12, "male", new Nested("books", "Music", "Movie"))));
public class Something {
#JsonProperty("name")
public String name;
#JsonProperty("age")
public int age;
#JsonProperty("gender")
public String gender;
#JsonProperty("interest")
public Nested nested;
//Constructor
}
public class Nested {
#JsonProperty("books")
public String books;
#JsonProperty("music")
public String music;
#JsonProperty("movie")
public String movie;
//Constructor
}
and the Output is:
{
"name":"Name",
"age":12,
"gender":"male",
"interest":
{
"books":"books",
"music":"Music",
"movie":"Movie"
}
}
So everything is working as expected. I already checked if theres a difference if you provide some setters and getters and setting the visibility of the fields on private but that doas not make a difference.
Maybe you want to show us your InterestPojo or your output/stacktrace?
EDIT:
Okay i think i got it ;)
I tried the following:
public void start() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().addMixInAnnotations(Something.class, Nested.class);
mapper.getDeserializationConfig().addMixInAnnotations(Something.class, Nested.class);
System.out.println(mapper.writeValueAsString(new Something("Name", 12, "male", new NestedImpl("name", null))));
}
public final class Something {
private final String name;
private int age;
private String gender;
// thats your interest thing
public Nested nested;
public Something(String name, int age, String gender, Nested nested) {
this.name = name;
this.age = age;
this.gender = gender;
this.nested = nested;
}
String getName() {
return name;
}
Nested getNested() {
return nested;
}
}
public abstract class Nested {
#JsonProperty("name-ext")
abstract String getName();
#JsonProperty("interest-ext")
abstract Nested getNested();
}
public class NestedImpl extends Nested {
private String name;
private Nested nested;
private NestedImpl(String name, Nested nested) {
this.name = name;
this.nested = nested;
}
#Override
String getName() {
return name;
}
#Override
Nested getNested() {
return nested;
}
}
Output:
{
"age":12,
"gender":"male",
"name-ext":"Name",
"interest-ext":
{
"name-ext":"name",
"interest-ext":null
}
}
Thats not exactly your structure, but I think thats what you want. Am I right?
EDIT2: I Tested the following structure with JSON->Object and Object->JSON.
ObjectMapper mapper = new ObjectMapper();
mapper.getSerializationConfig().addMixInAnnotations(Something.class, Mixin.class);
mapper.getSerializationConfig().addMixInAnnotations(Nested.class, NestedMixin.class);
mapper.getDeserializationConfig().addMixInAnnotations(Something.class, Mixin.class);
mapper.getDeserializationConfig().addMixInAnnotations(Nested.class, NestedMixin.class);
Nested nested = new Nested();
nested.setName("Nested");
nested.setNumber(12);
Something some = new Something();
some.setName("Something");
some.setAge(24);
some.setGender("Male");
some.setNested(nested);
String json = mapper.writeValueAsString(some);
System.out.println(json);
Something some2 = mapper.readValue(json, Something.class);
System.out.println("Object: " + some2);
public abstract class Mixin {
#JsonProperty("name")
private String _name;
#JsonProperty("age")
private int _age;
#JsonProperty("gender")
private String _gender;
#JsonProperty("interest")
private Nested nested;
}
public class Something {
private String _name;
private int _age;
private String _gender;
private Nested nested;
// You have to provide Setters and Getters!!
}
public abstract class NestedMixin {
#JsonProperty("nameNested")
private String name;
#JsonProperty("numberNested")
private int number;
}
public class Nested {
private String name;
private int number;
// You have to provide Setters and Getters!!
}
Output:
{"age":24,"gender":"Male","name":"Something","interest":{"nameNested":"Nested","numberNested":12}}
Object: Something{name=Something, age=24, gender=Male, nested=Nested{name=Nested, number=12}}
Note: It seems that jackson got problems with inner classes. So if you test that examples in an extra project create extra class-files ;)
EDIT3: If you are using a Module, try the following:
public class JacksonMixinModule extends SimpleModule {
public JacksonMixinModule() {
super("JacksonMixinModule", new Version(0, 1, 0, "SNAPSHOT"));
}
#Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.setMixInAnnotations(Something.class, Mixin.class);
context.setMixInAnnotations(Nested.class, NestedMixin.class);
}
}
...
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JacksonMixinModule());

Categories