41

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.

KAD
  • 10,972
  • 4
  • 31
  • 73
M.J.
  • 16,266
  • 28
  • 75
  • 97

3 Answers3

47

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

facundofarias
  • 2,973
  • 28
  • 27
Mohamed Mansour
  • 39,445
  • 10
  • 116
  • 90
  • hi, the above method of implementation is very good, i need to know, without adding those @tags, the above code will not work??? i mean apart from this, is there any other way.. – M.J. Mar 04 '11 at 05:40
  • What do you mean it does not work? Make sure you are using JDK 6. Well, I opened up VIM and used what I have demonstrated above, and it works just fine: https://gist.github.com/854233 – Mohamed Mansour Mar 04 '11 at 05:45
  • It had worked.. and worked without any error. Basically i wanted to know, whether there is any other way of doing so, as i cannot change the classes. – M.J. Mar 04 '11 at 07:01
  • Well, then you can tell it what the Element is using QName if your not using annotations: `m.marshal(new JAXBElement(new QName(Student.class.getSimpleName()), Student.class, student), writer);` I updated the code if you have no annotations. – Mohamed Mansour Mar 04 '11 at 14:46
  • 1
    Regarding XStream, check out: http://bdoughan.blogspot.com/2010/10/how-does-jaxb-compare-to-xstream.html – bdoughan Mar 04 '11 at 15:44
  • @Mohamed Mansour: Check out my answer to see how you can leverage JAXBSource to eliminate the intermediary XML representation: http://stackoverflow.com/questions/5189690/how-to-serialize-and-de-serialize-objects-using-jaxb/5195945#5195945 – bdoughan Mar 04 '11 at 16:24
45

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());
    }

}
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 1
    That is pretty nice, didn't know about JAXBSource very useful when serializing / deserializing in the same app. Thanks :) – Mohamed Mansour Mar 05 '11 at 07:10
  • @blaise : i guess i doesn't work for enums, if the object has enums, then it fails saying no default Constructor Defined. – M.J. Mar 07 '11 at 06:29
  • @Mrityunjay - enum properties should also work, I have updated my answer to include an enum. – bdoughan Mar 07 '11 at 16:16
  • @Blaise : with this enumeration, this code will work, but suppose u have a enum something like Stutus which have values PART_TIME("P"), FULL_TIME("F"), for this type of enum the conversion fails as the enumeration fails saying the default constructor not found, because in order to compile the enum , we need to put one Parameterized Constructor.. – M.J. Mar 09 '11 at 04:51
  • @Mrityunjay - I have been unable to reproduce the issue you are seeing. I have updated my answer with what you have tried so far. Could you let me know what I'm doing different that you? – bdoughan Mar 09 '11 at 14:30
  • @Blaise :i have one doubt, does this marshalling and unmarrshalling depends on the attribute name and getter and setters defined in an object??? – M.J. Mar 11 '11 at 06:33
  • @Mrityunjay - Without annotations the marshal/unmarshal operations will leverage all public fields (instance variables) and properties (get/set methods). You can use the @XmlAccessorType annotation to control this. – bdoughan Mar 15 '11 at 13:17
  • I guess the line `JAXBElement jaxbElementB = unmarshallerB.unmarshal(sourceA, com.school.Student.class);` does not compile – Giuseppe Adaldo Nov 20 '14 at 10:56
  • @GiuseppeAdaldo - It should compile, what error are you seeing? – bdoughan Nov 20 '14 at 11:51
  • 1
    You were right, I was using a different version. So, your solution is cool, Thank you. – Giuseppe Adaldo Nov 20 '14 at 12:40
2

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.

Kevin
  • 24,871
  • 19
  • 102
  • 158
  • this is a better approach, but can we write some generic code, which cam be used across objects, As my requirement is not with one object instead with multiple objects, so writing the same code again and again, for all objects is not a good thing, that is the only reason i thought of JAXB. – M.J. Mar 07 '11 at 04:43