I'm using JAXB to save objects to xml files.
#XmlRootElement(name="jaxbobjt")
#XmlAccessorType(XmlAccessType.FIELD)
public class SomeJAXBObject
{
#XmlElementWrapper(name="myEntry")
private Map<Integer, AnotherJAXBObject> map = Collections.synchronizedMap(new LinkedHashMap<Integer, AnotherJAXBObject>());
}
Note the fact that I'm using a synchronizedMap(...) wrapper.
The above results in the following xml:
<jaxbobjt>
<map>
<myEntry>
<key>key</key>
<value>value</value>
</myEntry>
</map>
</jaxbobjt>
Actually I thought that I would need an XmlAdapter to get this working.
But to my surprise this marshals and unmarshals fine. Tests revealed that it correctly uses a java.util.Collections$SynchronizedMap containing a LinkedHashMap$Entry object.
So, if I understand correctly. JAXB's unmarshaller, just instantiates my object using the constructor. Since there's already an instance for the map after instantiation of the object, it does not instantiate the map itself. It uses the putAll I assume ?
I'm just trying to get a deeper understanding of what is going on. It would be nice of somebody could give me some more background information about this. Are my assumptions correct ?
If I am correct, I assume the following implementation would have failed:
#XmlRootElement(name="jaxbobjt")
#XmlAccessorType(XmlAccessType.FIELD)
public class SomeJAXBObject
{
// no instance yet.
#XmlElementWrapper(name="myEntry")
private Map<Integer, AnotherJAXBObject> map = null;
public synchronized void addObject(Integer i, AnotherJAXBObject obj)
{
// instantiates map on-the-fly.
if (map == null) map = Collections.synchronizedMap(new LinkedHashMap<Integer, AnotherJAXBObject>());
map.put(i, obj);
}
}
The strategy used by JAXB is to create container classes only when it is necessary. For anything that is bound to a List, JAXB's xjc creates
protected List<Foo> foos;
public List<Foo> getFoos(){
if( foos == null ) foos = new ArrayList<>();
return foos;
}
and thus, unmarshalling another Foo to be added to this list, does essentially
parent.getFoos().add( foo );
As for maps: presumably the working version of your class SomeJAXBObject contains a getMap method, and that'll work the same way. Setters for lists and maps aren't necessary, and they'll not be used if present. A put method in the parent class isn't expected either; if present it'll not be used because JAXB wouldn't have a way of knowing what it does.
Related
At runtime I want to create a POJO with attributes that are same as keys of the map and populate it with the values of the map. I don't know the content of the map or what the POJO will look like.
Example-
Map<String,String> map = new HashMap<>();
map.add("attr1", obj1);
map.add("attr2", obj2);
...
From this map I want to create a POJO-
class POJO
{
String attr1;
public void setAttr1(String attr1) {
this.attr1 = attr1;
}
public String getAttr1() {
return attr1;
}
String attr2;
public void setAttr2(String attr2) {
this.attr2 = attr2;
}
public String getAttr2() {
return attr2;
}
.....
}
and populate it as well.
All of this should happen at runtime.
Something like-
Object object = getPopulatedPOJO(map)
or
Class type = getPOJOType(map);
Object object = type.newInstance();
object = getPopulatedPOJO(map)
This is not the final answer to your problem. But I hope this gives the direction you might want to continue with. Note that, this direction modifies bytecode instructions at runtime and can cause crashes and failures.
You can use javassist which is a bytecode manipulator. Please see their official site for more info.
Two important things to note
In Java, multiple class loaders can coexist and each class loader creates its own name space. Different class loaders can load different class files with the same class name
The JVM does not allow dynamically reloading a class. Once a class loader loads a class, it cannot reload a modified version of that class during runtime. Thus, you cannot alter the definition of a class after the JVM loads it. However, the JPDA (Java Platform Debugger Architecture) provides limited ability for reloading a class
So you can achieve what you want in two different ways.
Create bytecode at runtime, write the class, use a custom classloader to create your pojo from the written class. javassist can help you for this, but this is way too complicated for me to consume at the moment.
Use javassist to modify an existing class and use reflection to set the values.
For option 2, the easier one, here is how you can achieve this.
Add javassist in your classpath. If you are using maven, add the following dependency in your pom.xml.
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.21.0-GA</version>
</dependency>
Create a dummy empty pojo class that you need to work with. Let us call it Pojo.
package com.test;
public class Pojo {
//Nothing in the source file.
}
Modify the class body to add the fields from the HashMap. Here is a sample of how I did it using the map you gave.
Map<String, String> map = new HashMap<String, String>();
map.put("firstname", "John");
map.put("lastname", "Doe");
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.test.Pojo");
// Used for non-primitive data types. If primitive, use CtClass.<inttype, floattype, etc..>
CtClass strClass = ClassPool.getDefault().get("java.lang.String");
//Iterate and add all the fields as per the keys in the map
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
CtField field = new CtField(strClass, key, cc);
field.setModifiers(Modifier.PUBLIC);
cc.addField(field);
}
// Instantiate from the updated class
Class<Pojo> clazz = cc.toClass();
Pojo newInstance = clazz.newInstance();
//Use the map again to set the values using reflection.
iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
newInstance.getClass().getField(key).set(newInstance, map.get(key));
}
newInstance is the instance of Pojo but with fields added based on keys of the map and set based on the values in the map. A simple test to print the newInstance using jackson ObjectMapper yields this.
ObjectMapper objMapper = new ObjectMapper();
String writeValueAsString = objMapper.writeValueAsString(newInstance);
System.out.println(writeValueAsString);
{"firstname":"John","lastname":"Doe"}
Hope this helps.
EDIT
If you want to add get/set methods, you can create methods using CtMethod in javassist. However, you can only access them using reflection since these methods are added at runtime.
See the answer in a similar question, using Jackson objectMapper.convertvalue method seems most reasonable.
If I have a class
class DTO {
final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));
public MySet getValues() {
return values;
}
}
where MySet extends Set. Jackson complains that
Cannot find a deserializer for non-concrete Collection type MySet
which I understand, but I already instantiated the collection. What I want is for jackson to just call add for each value after it created an instance, something like:
DTO o = new DTO();
MySet<Types> values = o.getValues();
for (Types type : jsonArray) {
values.add(type );
}
I don't want it to try to create a new collection itself.
That error message means that the DTO class is configured (by default or explicitly) to deserialize the values part of the JSON input into the DTO values field of DTO :
Cannot find a deserializer for non-concrete Collection type MySet
If you consider that Jackson should not perform the deserialization directly on this field, you could define a constructor to set values and also make sure that Jackson will not perform automatically the deserialization work : to achieve it, remove setter for that field (or add #JsonIgnore on it) and any jackson module that will use reflection to deserialize to fields.
It would give :
final MySet<Types> values = MySetWrapper(EnumSet.of(Types.class));
#JsonCreator
public MyFoo(Set<Types> values) {
this.values.addAll(values);
}
Note that I specified in the constructor Set and not MySet (should not be an issue as interface doesn't declare fields), otherwise you would get the same issue since you didn't define a deserializer for MySet.
But if you implement a deserializer for that you could of course do :
public MyFoo(MySet<Types> values) {
this.values.addAll(values);
}
Found an answer using #JsonProperty:
#JsonProperty
private void setValues(Set<Types> types) {
values.addAll(types);
}
Pretty short and simple thankfully.
Edit: seems like you don't even need the annotation.
How to create a class that add some stuff to a map and display it.
I was wondering about the best interface considering that all the code usually need to be covered with unit tests and it's a problem to test a method that display data.
This was my first thought:
class MyFirstProgram {
private Map<String, String> map = new HashMap<String,String>();
public int insertData(...) {...}
public void displayData(...) {...}
}
...but it's not possible to test anything about the retrieval and there is this display method... so I thougt this:
class MyFirstProgram {
private Map<String, String> map = new HashMap<String,String>();
public int insertData(...) {...}
private Map<String, String> retrieveData(...) {...}
public int displayData(...) {...call ; return status}
}
In this it's possible to test the private method with reflection but there is still this display method...
Any idea about the design?
The pragmatic way is to allow access to the Map (via a default/package access method with a comment // for unit tests, and access that. Actually, even better, name the method forUnitTestGetMap() to make it extra clear and avoid confusion with the standard getXXX naming convention. I'm usually fine with just calling toString() on the Map and comparing to what it should be, YMMV. If your insertData() is just calling standard methods on HashMap (e.g. put()) there isn't that much you can and should test, as most of the code is Java library code.
Alternatively, in the displayData() method, does it create an alternative representation of the Data? Such as XML, JSON, maybe a JPanel? If so, look at that to see that the contents of the Data are correct. Obviously XML is much easier than a JPanel for that, but you can still do something like checking that the JPanel has 3 children and the first is a Button named "OK"...
You can try creating getter and setter for the Map variable.
class MyFirstProgram {
private Map<String, String> map = new HashMap<String,String>();
public setMap(Map data) {...}
public Map getMap(...) {...}
}
And then wherever you want to print the map value use getMap method.
If you don't want to use the setters and getters we can have any method which takes a Map as input and print it.
public void PrintMap(Map data){print(data)}
I try to serialize embedded collection using simple.
For example :
Map<String, List<MyClass>>
I already added necessary annotations in MyClass, i tried with #ElementMap but it doesn't work:
Exception in thread "main" org.simpleframework.xml.transform.TransformException: Transform of class java.util.ArrayList not supported
If its just
#ElementMap Map<String, MyClass>
it works fine. I don't know ho to deal with embedded collection. I know about #ElementList annotation but don't know how to use it in this case. Any hints?
I'm coming across the same issue. The only way I have managed to get it working has been a really cheesy hack - wrapping List in another class.
public class MyWrapper {
#ElementList(name="data")
private List<MyClass> data = new ArrayList<MyClass>();
public MyWrapper(List<MyClass> data) {
this.data = data;
}
public List<MyClass> getData() {
return this.data;
}
public void setData(List<MyClass> data) {
this.data = data;
}
}
And then, instead of
#ElementMap Map<String,List<MyClass>>
...you'd have:
#ElementMap Map<String,MyWrapper>
In my case, the Map is entirely private to my class (i.e. other classes never get to talk directly to the Map), so the fact that I have this extra layer in here doesn't make much of a difference. The XML that is produced of course, is gross, but again, in my case, it's bearable because there is nothing outside of my class that is consuming it. Wish I had a better solution than this, but at the moment, I'm stumped.
I have a class called DataSet with various constructors, each specifying a different type of variable. It might look a bit like this:
public class DataSet
{
private HashSet Data;
public DataSet( DataObject obj )
{
Data = new <DataObject>HashSet();
Data.add( obj );
}
public DataSet( ObjectRelationship rel )
{
Data = new <ObjectRelationship>HashSet();
Data.add( rel );
}
// etc.
Note: I haven't yet gotten to test that code due to incomplete parts (which I'm building right now).
In a function that I'm currently building, getDataObjects(), I need to return all DataObject objects that this set represents. In the case of constructors that initiate the class's HashSet, Data with types other than DataObject (such as the above ObjectRelationship), there obviously won't be any DataObjects stored within. In this case, I need to be able to detect the type that the HashSet 'Data' was initiated with (like, to tell if it's 'ObjectRelationship' or not, I mean). How do I do this?
tl;dr: How do I tell the type that a Collection (in this case, a HashSet) was initiated with in my code (like with an 'if' or 'switch' statement or something)?
Sounds like you want to make the entire class generic- add a template parameter to the declaration for the class and define your HashSet and retrieval functions using that template parameter for the types.
I'm a .Net guy at the moment, though, so I couldn't give you the Java syntax, but using C# syntax it would look something like this:
public class DataSet<T>
{
private Set<T> Data;
public DataSet( T obj )
{
Data = new HashSet<T>();
Data.add( obj );
}
public Iterator<T> getDataObjects()
{
return Data.iterator;
}
}
You could fetch an object from the set and verify its type.
Or you could have multiple sets to contain different types.
Or you could have an instance variable of type Class to act as a discriminator as an instance variable.
Or you could create a proxy object for HashSet using the last technique.
You could use a map to the set
HashMap <Class<?>, HashSet<Object>> data;
HashSet temp = data.get(DataObject.class);
if(temp == null)
{
temp = new HashSet();
data.put(DataObject.class, temp);
}
temp.add(obj);
Then you will get the best of both worlds.
Sounds like your design needs to be re-thought.
Also, to be clear on Generics; you cannot access the type at runtime. The type parameter is only for compile-time checking and is completely gone (type erasure) at runtime.
What does this class offer that CachedRowSet does not?
Sorry, I don't consider this to be a very good abstraction. If I were a member of your team, I wouldn't use it.
Your syntax doesn't look correct to me, either. IntelliJ agrees with me: it won't compile.
This does:
import java.util.HashSet;
import java.util.Set;
import java.util.Arrays;
public class DataSet
{
private Set<DataObject> data;
public DataSet(DataObject obj)
{
this.data = new HashSet<DataObject>();
data.add(obj);
}
public DataSet(DataObject[] objs)
{
data = new HashSet<DataObject>();
data.addAll(Arrays.asList(objs));
}
// etc.
}
Still a poor abstraction. Rethink it.
You could add an property to your dataset class (an enumerated value, boolean or type) that specifies which type was used to initialize the hashset.
Set the property in the appropriate constructor. This allows you to bypass getting an element out of the collection to check its type.
pseudo-code:
public class DataSet
{
private HashSet Data;
private Type _iw = null;
public Type InitializedWith { return _iw; }
public DataSet(DataObject)
{
...
_iw = typeof(DataObject);
}
public DataSet(ObjectRelationship)
{
...
_iw = typeof(ObjectRelationship)
}
I'm going to follow duffymo's advice and just use better abstraction. I'm going to make multiple classes for each specific type I plan to use (each implementing a common interface) so that I can just bypass this dumb problem.
It'll add a minuscule bit of overhead during the process of creating each DataSet object of correct type, but I suppose that's just how it goes.
I don't know what DataObject gives you over and above an Object.
I think an object-oriented approach to your problem would use classes that reflected your domain of interest (e.g., Invoice, Customer, etc.). The persistence layer would hide the persistence details.
A common way to accomplish this is to use the Data Access Object, which might look like this in Java:
public interface GenericDao<T>
{
T find(Serializable id);
List<T> find();
void save(T obj);
void update(T obj);
void delete(T obj);
}
Now you're dealing with objects instead of things that smack of relational databases. All the CRUD details are hidden behind the DAO interface.