Skip to main content

Arrays and XmlJavaTypeAdapter

11 replies [Last post]
dprunier
Offline
Joined: 2008-02-12

Hi,

I was going trhough the example "XmlAdapter in JAXB RI EA" (http://weblogs.java.net/blog/kohsuke/archive/2005/04/xmladapter_in_j.html) but there is something i don't understand.

It seems that:

@XmlJavaTypeAdapter(CourseListAdapter.class)
Map courses;

is not the exact equivalent of:

Course[] courses;

For example, if i marshall the following class

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Brochure {
@XmlJavaTypeAdapter(CourseListAdapter.class)
private Map coursesMap;
private Course[] courseArray;
}

I get something like this:

cn1

cn2

cn1

cn2

Does anybody know how to get rid of this item element when using XmlJavaTypeAdapter ?

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
dharland
Offline
Joined: 2006-10-17

You are correct that the id values you see in the XML come from the value objects of the map, not the keys.

JAXB is actually unaware of the Map; it is dealing with an array of type Course. The get/setXmlCourses methods take our internal storage -- a Map -- and turn it into something jaxb can understand -- an array, completely hiding the map from jaxb. Thus, no XML for the keys.

I tend to use methods like this if the map key is already a data item of the value object itself. If it's not, though, then i'd use something like the MapPair example shown in Kohsuke's blog (http://weblogs.java.net/blog/kohsuke/archive/2005/04/xmladapter_in_j.html).

michelvdl
Offline
Joined: 2008-04-01

I think a dim candle is flickering on for me now.

I assume the get/setXmlCourses methods are a jaxb convention to deal with the original getCourses/setCourses classes.

I guess one reason for my confusion is the lack of a clear definition what java objects map to wsdl sequences (only arrays?).

dharland
Offline
Joined: 2006-10-17

"I assume the get/setXmlCourses methods are a jaxb convention to deal with the original getCourses/setCourses classes."

Not that i'm aware of. In my real code i sometimes find that i want public get/setAbc... methods that return maps, or something else that i don't want jaxb touching directly, but that "Abc" is also the natural xml element names. Just to help me remember why these seemingly uncalled private methods exist, i've taken to putting Xml in the names, along w/ adding comments to that effect. (I also tag the public methods w/ @XmlTransient to make jaxb ignore them.)

As far as jaxb and collections, i know i have a fair amount of lists that jaxb handles nicely. It took me a little while to get my annotations correct for dealing w/ lists of polymorphic objects, but once past that hurdle, things had been working well. I say "had been" because w/ the latest jdk updates, which apparently changed from using jaxb 2.0.x to 2.1.x, i ran into troubles getting lists from xml back into objects. If interested, see this issue: https://jaxb.dev.java.net/issues/show_bug.cgi?id=488

michelvdl
Offline
Joined: 2008-04-01

That sample is still broken in 1.6.0_10-beta-b14.

johnmax
Offline
Joined: 2008-10-08

great example help us lot

Message was edited by: johnmax

dprunier
Offline
Joined: 2008-02-12

Great, this is quite obvious actually ! Thanks.

dharland
Offline
Joined: 2006-10-17

I have an alternative for you that does not use the type adapter. Like your example, the key of the map is also a property of the value object (here, the "id" of Course).

[code]
package dmh.mapAdapter;

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
*
* Forum question
.
*
* Kohsuke's original example
.
*/
@XmlRootElement(name="brochure")
public class Brochure
{
private Map courses = new HashMap();

public Map getCourses() { return courses; }

public static void main(String... args) throws Exception
{
Brochure brochure = new Brochure();

brochure.courses.put("5.42", new Course("5.42", "Organic Chemistry"));
brochure.courses.put("8.02", new Course("8.02", "Physics"));
brochure.courses.put("18.02", new Course("18.02", "Calculus"));

JAXBContext jaxb = JAXBContext.newInstance(Brochure.class);

Marshaller xmlConverter = jaxb.createMarshaller();
xmlConverter.setProperty("jaxb.formatted.output", true);
xmlConverter.marshal(brochure, System.out);
}

//Used only for JAXB. Helps deal w/ the underlying map.
//@XmlElementWrapper(name = "courses") //Activate if you want "courses" wrapper
@XmlElement(name="course")
private Course[] getXmlCourses()
{
Course[] result = new Course[courses.size()];

int e=0;
for (String id : courses.keySet())
result[e++] = courses.get(id);

return result;
}

//Used only for JAXB. Helps deal w/ the underlying map.
private void setXmlCourses(Course[] replacements)
{
courses.clear();
for (Course c : replacements)
courses.put(c.id, c);
}
}

class Course
{
@XmlAttribute String id;
@XmlElement String name;

Course() { this("dummy", "dummy"); }
Course(String id, String name) {this.id=id; this.name=name; }
}
[/code]
Output:

[code]



Calculus


Organic Chemistry


Physics


[/code]

If you activate the @XmlElementWrapper tag, you'll get a "courses" container for the "course" elements inside of the "brochure" element.

michelvdl
Offline
Joined: 2008-04-01

I was studying this example. I'm new at this, so feel free to send me to the right spot if this is a dumb question. Looking at the result of this, I would have expected something where I see the keys twice, given the code puts it into a Map like so:

brochure.courses.put("5.42", new Course("5.42", "Organic Chemistry"));

So, the course Map has a key 5.42, and than the course has a declared XmlAttribute id, which I see in the result as:

But why don't I see the key of the courses Map?

dprunier
Offline
Joined: 2008-02-12

Here it is (in setXmlCourses):

[code]
courses.put(c.id, c);
[/code]
Obviously, having a direct access to the map (via getCourses()) may introduce inconsistencies. In this example, the user is reponsible for doing a correct put operation, which is what you expect: [code]map.put("5.42", new Course("5.42", "Organic Chemistry"));[/code]
In my actual implementation, i don't expose the map and provide access methods (e.g. getCourse(id)).

Hope this helps.

michelvdl
Offline
Joined: 2008-04-01

Thanks for the quick reply.

I guess I should have be more precise in my question. How does JAXB decide to use the setXmlCourses/getXmlCourses methods? I've been trawling through all the docs, but that remains a bit of a mystery to me.

dprunier
Offline
Joined: 2008-02-12

See the @XmlAccessorType annotation.