Using a Class with Generics with GSON - java

I am hoping to get some direction on how to resolve this issue. I am working with a Third Party API get JSON data. It has a structure similar to:
{items: [
{attribute : Value},
{attribute : Value}]
count : value,
etc
}
The items array can hold different data, so I have a class:
public Items<T> {
private List<T> items;
// Others API Variables
public List<T> getItems(){
return items;
}
public void setItems(List<T> items){
this.items = items;
}
// Other Getters/Setters
}
What I am trying to do in the calling class is:
public CallingClass {
public void Method(){
//Code to get reader object
Items<User> userItems = Gson().fromJson(ReaderObject, Items.class);
//Other processing code
}
}
And I get the error:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to User.class
I was reading through this post and I think the solution is similar to what I need but I don't know who to do it. My though was to have a single Items class that could be passed the different Types which are returned.
Thoughts, assistance, anything would be helpful.
EDIT:
OK So I feel like an idiot now, but some sleep helped. The solution was in the link I posted, I was just didn't need a list. My coded solution is as follows:
public CallingClass {
public void Method(){
//Code to get reader object
Items<User> userItems = Gson().fromJson(ReaderObject, new TypeToken<Items<User>>(){}.getType());
//Other processing code
}
}

OK So I feel like an idiot now, but some sleep helped. The solution was in the link I posted, I was just didn't need a list. My coded solution is as follows:
public CallingClass {
public void Method(){
//Code to get reader object
Items<User> userItems = Gson().fromJson(ReaderObject, new TypeToken<Items<User>>(){}.getType());
//Other processing code
}
}

Related

Java - One Method for Many Objects

Say I've got a Java file with 500 custom objects of type Item. Now, say I want users to be able to add/remove any one of those objects to that list in the program. I want to try to avoid doing something like this:
public class Inventory {
public static ArrayList<Item> inv = new ArrayList<>();
public static void addItem1 {
inv.add(Item.Item1); //Pulling from Item class
} //addItem1()
public static void removeItem1 {
inv.remove(Item.Item1);
} //removeItem1()
public static void addItem 2 {
. . .
}
. . .
}
By doing that, I'd have to make an add and a remove function for every single item. There will be hundreds of items, so I sincerely hope there's a better way for the user to be able to do so from inside of the program. This would further be awful because of how it would require some massive nested switch statements to swap out everything.
Instead I'd hope to implement a single adder method and a single remover method, that could take the user's input (a String with the literal name of the Item they are trying to add), and somehow find/select the Item. Implementation thoughts?
How about using generic class ?
public class Inventory<T> {
public static ArrayList<Item> inv = new ArrayList<>();
public void addItem (T item){
inv.add((Item)item); // where item can be anything from Item.item1 to Item.item500
}
public void removeItem (T item){
inv.remove((Item)item);
}
In that case, to see if your item is in fact an item do something similar to this: System.out.println(item.getClass().getName()); //should return Item
Perhaps use:
public void setItem(Item item){
inv.add(item);
}
Then use the same concept for removal.

JAXB Generic XmlAdapter implementation

This question continues my previous thread : here
Now i want to use a generic XmlAdapter, because i need to apply the same work on differents type of value. I can create one class per type, but that's not the purpose of my question, i want to make this generic. So this is what i did :
Generic Adapter Class
public class GenericXMLAdapter<T> extends XmlAdapter<GenericXMLAdapter.AdaptedValue<T>, T>{
public static class AdaptedValue<T> {
#XmlAttribute
public T code;
}
#Override
public T unmarshal(AdaptedValue<T> v) throws Exception {
return v.code;
}
#Override
public AdaptedValue<T> marshal(T v) throws Exception {
AdaptedValue<T> adaptedValue = new AdaptedValue<T>();
adaptedValue.code = v;
return adaptedValue;
}
}
My temporary class to generate the correct Adapter
public final class DefinedXMLAdapter {
public static class BooleanAdapter extends GenericXMLAdapter<Boolean> {};
}
Example to marshall
#XmlElement(name = "theBoolean")
#XmlJavaTypeAdapter(DefinedXMLAdapter.BooleanAdapter.class)
protected Boolean myBoolean = false;
When i execute my code, i get a weird error i can't understand :
Exception in thread "main" java.lang.NullPointerException
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:154)
...
Can someone explains to me what's wrong with my code and how to resolve this issue ?
I finally managed to solve my problem by using MOXy as the JAXB implementation.
Thanks for your time, have a good day!

How to pass list of objects to a function

I want to pass a List of objects to one of my functions.
Here's the code:
#RequestMapping(value = "/pages/generateReports")
public ModelAndView toGenerateReports(#ModelAttribute("forReports") #Valid Reports report){
List<Patients> patients = patientsDAO.listAllPatients(report.getDateFrom(), report.getDateTo());
//tried printing what's in patients and surely it contains something
GenerateReports reports = new GenerateReports();
reports.generateReports(patients);
return ...;
}
public class GenerateReports {
public void generateReports(List<? extends Patients> patients) {
....some checkings here and all I get is an empty list...
}
}
What do I have to do to make it work?
Your code looks good to me, it should work. You can change List<? extends Patients> patients to List<Patients> patients but that should not change anything.

Casting objects when returning a generics list

I'm relatively new to Java and generics. I'm trying to understand if I'm doing something wrong or not in writing a generic method. I have the following code (greatly simplified):
public class ContentIniter {
public ContentType getContentType();
}
public interface Content {
}
public class Show implements Content {
}
public class Movie implements Content {
}
public enum ContentType {
Movie, Show
}
public class Channel {
public List<Show> getShows() {
return getContentByType(ContentType.Show)
}
public List<Movie> getMovies() {
return getContentByType(ContentType.Movie)
}
private <T> List<T> getContentByType(ContentType contentType) {
List<T> typeContents = Lists.newArrayList();
List<ContentIniter> allContentIniters = someMethod(); // Returns initers for both shows and movies
for (Content contentIniter : allContentIniters) {
if (contentIniter.getContentType().equals(contentType)) {
switch (contentType) {
case Movie:
typeContents.add((T) new Movie(contentIniter));
break;
case Show:
typeContents.add((T) new Show(contentIniter));
break;
}
}
}
return typeContents;
}
}
My question relates to the line:
typeContents.add((T) new Movie(contentIniter));
The only way I've been able to get the code to compile is if I cast the content object to T. But that seems yucky to me (and I don't understand why the compiler can't infer the type based on the calls). Moreover, even though the code works, IntelliJ complains of an unchecked cast.
Is there a better way to write the generic method?
UPDATE: Screwed up the code a bit when I tried to simplify it. Fixed the reference to typeContents. Also, I added a bit more complexity so that it better reflects the reality, in hopes of explaining why I wasn't simply checking for instanceof.
UPDATE 2: Realized there was yet another error...ContentIniter doesn't implement Content. It's also worth noting, ContentIniter is just a made up object. If it seems weird, think of it as an Event or other Strategy that Content objects use to delegate certain behaviors.
You're not using generics properly, you're mixing them with your enumeration when it's really not necessary. Ideally you would be calling getContentByType<Show>() and then determine the list of the correct type from allContents using reflection.
Try something more along the lines of like (untested):
private <T> List<T> getContents() {
List<T> typeContents = Lists.newArrayList();
List<Content> allContents = someMethod(); // Returns both shows and movies
for (Content content : allContents) {
if (content instanceof T) {
typeContents.add((T) content);
}
}
return typeContents;
}
And call:
List<Show> shows = getContents<Show>();
You can then restrict the types that are called on it to only those that extend Content.
private <T extends Content> List<T> getContents() {
...
}
Actually the answer is simpler than you think : you just have to check whether your instance of Content is a Show or a Movie to make your compiler happy :
if (content instanceof Movie)
contents.add((Movie) content);
if (content instanceof Show)
contents.add((Show) content);
Anyway, I would say that the way that you wrote your generic method is correct. But since there is a native way to check for the type of an instance (instanceof), you should use it :)
EDIT : I still think you should use instanceof.
Plus, you should use a List<Content> instead of a List<ContentIniter>, because Content is a more global type : if someone comes up with another implementation of Content, he won't have to change your code. Actually, you're doing the same thing when you use the Interface List instead of an ArrayList for example, because List is less specific than ArrayList.
Also, using an enum is not a mistake : if you want to use one, you can. But it shouldn't be used to determine the type of an instance. The type of an instance is contained in the instance itself, period. Still, I'll say that Daniel Imms' solution is more elegant than mine, and takes better advantage of Java type features.
public interface Content {
public STContentType getContentType();
}
public class ContentIniter implements Content {
}
// You can keep the enum, as long as it's not used
// to check for the type of an instance of ContentIniter
public enum ContentType {
Movie, Show
}
public class Show implements Content {
}
public class Movie implements Content {
}
public class Channel {
public List<Show> getShows() {
return getContentByType(ContentType.Show)
}
public List<Movie> getMovies() {
return getContentByType(ContentType.Movie)
}
private <T> List<T> getContentByType(ContentType contentType) {
List<T> typeContents = Lists.newArrayList();
// Using more generic type Content
List<Content> allContentIniters = someMethod(); // Returns initers for both shows and movies
for (Content contentIniter : allContentIniters) {
// If it's a Show and I asked for Shows
if (contentIniter instanceof Show && contentType == ContentType.Show)) {
typeContents.add(contentIniter);
}
// If it's a Movie and I asked for Movies
if (contentIniter instanceof Movie && contentType == ContentType.Movie){
typeContents.add(contentIniter);
}
}
return typeContents;
}
}
Use of enum seems strange here and the way you do lost advantage of using generics.
The initer things is making things even more strange and messy.
It may looks more natural with something like this:
public interface Content {
}
public class Show implements Content {
}
public class Movie implements Content {
}
//......
private <T extends Content> List<T> getContentByType(Class<T> contentType) {
List<T> result = Lists.newArrayList();
List<Content> allContents = someMethod(); // ContentIniter is just a mess
// Get all content you have!
for (Content content: contents) {
if (contentType.isAssignableFrom(content.getClass())) {
result.add(content);
}
}
return result;
}
The way to use is
List<Show> result = channel.getContent(Show.class);
I removed my original answer after the code example change.
I really don't think you can avoid the cast and the #SuppressWarnings("unchecked").
As long as you know what you're doing, this is probably the best solution.
The alternative is to do without the getByContentType method and just have a bit of duplicated logic on the getShows() and getMovies() methods.
For example:
public List<Show> getShows() {
List<Show> shows = new ArrayList<Show>();
List<ContentIniter> allContentIniters = someMethod();
for(ContentIniter initer: allContentIniters) {
if(initer.getContentType().equals(ContentType.Show)) {
shows.add(new Show(initer));
}
}
return shows;
}
public List<Movie> getMovies() {
List<Movie> movies = new ArrayList<Movie>();
List<ContentIniter> allContentIniters = someMethod();
for(ContentIniter initer: allContentIniters) {
if(initer.getContentType().equals(ContentType.Movie)) {
movies.add(new Movie(initer));
}
}
return movies;
}

How to get MessageBodyWriter to work with a HashMap using RestEasy and Tomcat?

I am developing a JAX-RS webservice using RestEasy 2.2.2 to be deployed to Tomcat 7. The service returns (should return) XML through the use of JAXB. Returned XML should contain representations of ConcurrentHashMap similar to how it is used in the following code:
#XmlRootElement(name="items")
#XmlAccessorType(XmlAccessType.NONE)
public class ItemCollection
{
#XmlElement(name="item")
private ConcurrentHashMap<String, Item> items;
public ItemCollection()
{
items = new ConcurrentHashMap<String, item>();
// fill the map
}
}
The Item class also contains a ConcurrentHashMap that needs to be serialized to XML.
This is the resource class:
#Path("/items")
public class ItemResource
{
#GET
#Produces(MediaType.APPLICATION_XML)
public ItemCollection getAllItems()
{
// get itemManager
return itemManager.getItems(); // ItemManager holds an instance of ItemCollection
}
}
This code runs, but produces an XML with no content:
<items>
<item/>
</items>
What I am trying to get as an output is something like:
<items>
<item id="...">
<data>...</data>
<otheritems>
<otheritem id="...">
<someotherdata>...</someotherdata>
</otheritem>
</otheritems>
</item>
<item id="...">
<data>...</data>
<otheritems>
<otheritem id="...">
<someotherdata>...</someotherdata>
</otheritem>
<otheritem id="...">
<someotherdata>...</someotherdata>
</otheritem>
<otheritem id="...">
<someotherdata>...</someotherdata>
</otheritem>
</otheritems>
</item>
</items>
I found out that a MessageBodyWriter implementation is required where builtin functionality is not sufficient. I tried to come up with a MessageBodyWriter implementation to marshal the ConcurrentHashMap, but I have not been able to get it working so far (i.e. I can get the code being called, but it stops with various exceptions).
It seems I do not quite grasp how MessageBodyWriter (and MessageBodyReader) interfaces should be implemented and used. I have Bill Burke's "RESTful Java with JAX-RS" book. It is very useful in terms of helping design JAX-RS services but I could not find enough details about the MessageBodyWriter functionality in the related section. My internet searches did not yield anything that could guide me in the right direction, either.
I would appreciate if anyone can help me in figuring out how to implement the MessageBodyWriter (and MessageBodyReader) interfaces properly. I do not know if I am missing an annotation, misplacing one or whether I need a completely new approach.
Thanks in advance for helping.
EDIT:
Modifying the code to following gets me halfway there:
#XmlRootElement(name="items")
#XmlAccessorType(XmlAccessType.NONE)
public class ItemCollection
{
private ConcurrentHashMap<String, Item> items;
public ItemCollection()
{
items = new ConcurrentHashMap<String, item>();
// fill the map
}
#XmlElement(name="item")
public Collection<Item> getItems()
{
return items.values();
}
}
This generates the XML that I need (sample included above). However, this code does not work when it comes to unmarshalling. I get the following exception:
java.lang.UnsupportedOperationException
java.util.AbstractCollection.add(AbstractCollection.java:221)
com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:290)
com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:254)
com.sun.xml.internal.bind.v2.runtime.unmarshaller.Scope.add(Scope.java:106)
com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty$ReceiverImpl.receive(ArrayERProperty.java:195)
com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(UnmarshallingContext.java:507)
com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:145)
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:601)
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1782)
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2938)
com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:200)
com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:173)
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:137)
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:142)
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:151)
javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:169)
// ... the rest
I assume the reason is the lack of a "proper setter" so that the unmarshaller does not try to add items into a Collection but I do not know how that setter would look like. If anyone knows how to do this, I would appreciate the help.
Thanks in advance.
EDIT 2:
BTW, I have seen Chris' reply where he suggests using #XmlJavaTypeAdapter. I have tried the suggestion and it gets me close to the XML I need. However, the XML I get using #XmlJavaTypeAdapter has an extra level in it (<class><items><item> instead of <items><item> --- as seen in my examples, I have the ConcurrentHashMap instances as a member variable of a class). I also can't seem to change the element name of the individual map items (they are always called "item").
These are not big problems and I can make the necessary changes and live with them if necessary. If possible, however, I would like to not have them in the first place. For educational purposes, I would also like to understand why the code in EDIT 1 does not work for unmarshalling (and how to fix it, if possible).
Thanks in advance for all the help.
I think your problem is that you are trying to mix JAXB with your MessageBodyReader/MessageBodyWriter. You have a JAXB object so you do not really want RestEasy to do all of the serialization using your MessageBodyWriter as it would not take into account your JAXB objects. You could do it this way, but you would need to serialize the rest of your object model too.
The MessageBodyReader/Writer is geared for working with Streams which is probably why it did not make much sense. It makes no assumptions that you are going to XML.
What you probably want to do is create an JAXB XmlJavaTypeAdapter for the map and let JAXB do the XML creation. You can find more information on the JavaDoc page:
http://download.oracle.com/javase/6/docs/api/javax/xml/bind/annotation/adapters/XmlJavaTypeAdapter.html
I did find one good post on this over the Metro mailing list here. This code should give you what you want. The context was around JAX-WS but you are looking specifically for the JAXB annotation to do a custom binding.
#XmlRootElement
public class Root
{
private String name;
private int junk;
#XmlJavaTypeAdapter(MapAdapter.class)
private Map<String, Boolean> lights;
public Root()
{
name = ""; junk = 0; lights = new HashMap<String, Boolean>();
}
public void setName(String newName) { name = newName; }
public String getName() { return name; }
public void setJunk(int newJunk) { junk = newJunk; }
public int getJunk() { return junk; }
public void turnLightOn(String lightName) { lights.put(lightName, true); }
public void turnLightOff(String lightName) { lights.put(lightName, false); }
}
class MapAdapter extends XmlAdapter<MapElements[], Map<String, Boolean>>
{
public MapElements[] marshal(Map<String, Boolean> arg0) throws Exception
{
MapElements[] mapElements = new MapElements[arg0.size()];
int i = 0;
for (Map.Entry<String, Boolean> entry : arg0.entrySet())
mapElements[i++] = new MapElements(entry.getKey(), entry.getValue());
return mapElements;
}
public Map<String, Boolean> unmarshal(MapElements[] arg0) throws Exception
{
Map<String,Boolean> r = new HashMap<String,Boolean>();
for(MapElements mapelement : arg0)
r.put(mapelement.key, mapelement.value);
return r;
}
}
class MapElements
{
#XmlElement public String key;
#XmlElement public Boolean value;
private MapElements() {} //Required by JAXB
public MapElements(String key, Boolean value)
{
this.key = key;
this.value = value;
}
}
The XmlJavaTypeAdapter method works OK in that it allows JAXB do the serialization and back. However, it does not give me the XML that I need. I thought that would not matter too much, but at the end it turned out that I need to get the XML in that form.
The XML I get using #XmlJavaTypeAdapter has an extra level in it (similar to <collection><mapitem><item><mapitem><item> instead of <collection><item><item>). I found a post on a forum (I will add the link if I can find it again) that explains this issue and indicates that in order to get this type of XML, JAXB needs to see a collection, rather than a map.
So, with the hope that it will be useful to others, here's what I did:
First, I defined this interface:
public interface MyCollectionInterface
{
public String getItemId();
}
Then I modified the items to be put in the collection as follows:
public class CollectionItem implements MyCollectionInterface
{
#XmlAttribute
private String id; // This was already a class member
#Override
public String getItemId()
{
return id;
}
}
The idea is to have a known way to get the key to be used with the HashMap.
Then came the declaration for MyCollection
public class MyCollection<E extends MyCollectionInterface> extends AbstractSet<E>
{
private ConcurrentHashMap<String, E> items;
public MyCollection()
{
items = new ConcurrentHashMap<String, E>();
}
#Override
public boolean add(E e)
{
return items.putIfAbsent(e.getItemId(), e) != null;
}
#Override
public Iterator<E> iterator()
{
return items.values().iterator();
}
#Override
public int size()
{
return items.size();
}
// other functionality as needed
}
Now, modifying the code to the following form puts everything into place:
#XmlRootElement(name="items")
#XmlAccessorType(XmlAccessType.NONE)
public class ItemCollection
{
#XmlElement(name="item")
private MyDictionary<CollectionItem> items;
public ItemCollection()
{
items = new MyDictionary<CollectionItem>();
}
}
This produces the XML I was after (see my original post for an example).

Categories

Resources