Certain specifications of my project require me to create POJO classes from information provided via an excel sheet or JSON; then creating objects of that class with relevant information at hand that will be used later in the code.
Extracting relevant data from excel sheets and JSON is not an issue. I was even able to create POJO classes dynamically thanks to this answer. But I'm unsure if it is possible to create objects of this class. As this guy mentioned in his above answer that -
But the problem is: you have no way of coding against these methods,
as they don't exist at compile-time, so I don't know what good this
will do you.
Is it possible to instantiate the class created in the above answer? If so, how? If not, what are other alternatives to this problem? Or should I change my approach regarding this specification and think of some other option?
You can use reflection to instantiate the generated classses and access the provided methods.
Probably in your situation I would go for something like below. This could not be post as a comment. So posting here.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GenericDTO {
private Map<String, List<Object>> resultSetMap = new HashMap<String, List<Object>>() ;
public void addAttribute(String attributeName, Object attributeValue) {
if(resultSetMap.containsKey(attributeName)) {
resultSetMap.get(attributeName).add(attributeValue);
} else {
List<Object> list = new ArrayList<Object>();
list.add(attributeValue);
resultSetMap.put(attributeName, list);
}
}
public Object getAttributeValue(String key) {
return (resultSetMap.get(key) == null) ? null : resultSetMap.get(key).get(0);
}
public List<Object> getAttributeValues(String key) {
return resultSetMap.get(key);
}
}
You can use it like:
GenericDTO dto = new GenericDTO();
dto.addAttribute("aa", 1);
dto.addAttribute("aa", "aa");
dto.addAttribute("bb", 5);
System.out.println(dto.getAttributeValue("bb"));
System.out.println(dto.getAttributeValues("aa"));
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.
I am getting the below error for HashMap in the Java code.
Error - "The type HashMap is not generic; it cannot be parameterized with arguments <>"
package com.example.map;
import java.util.Map;
import java.util.HashMap;
public class HashMap {
public static void main(String[] args) {
// compilation error here vvvvvvv
Map<Integer, String> mapHttpErrors = new HashMap<>();
mapHttpErrors.put(200, "OK");
mapHttpErrors.put(303, "See Other");
mapHttpErrors.put(404, "Not Found");
mapHttpErrors.put(500, "Internal Server Error");
System.out.println(mapHttpErrors);
}
}
You have named your own class HashMap as well.
When you write new HashMap the compiler thinks you're referring to your own class which indeed does not specify any generic parameters.
You can (and in fact: should) either change the name of your class to something else or explicitly refer to java.util.HashMap in your code:
Map<Integer, String> mapHttpErrors = new java.util.HashMap<>();
As the error is telling you, your HashMap class isn't generic, so your code makes no sense.
You should not make classes with the same names as built-in classes.
In the following line HashMap refers to the public class you created:
Map<Integer, String> mapHttpErrors = new **HashMap**<>();
Naming your class with exact the same name as classes from official Java API is often a very bad idea. But if you're sure that instead of that, you still want to keep the old name, here is the way you can use the HashMap from java.util package:
Map<Integer, String> mapHttpErrors = new java.util.HashMap<>();
But again, remember that naming your classes as you did in your program is rather a bad idea.
I have an owl file and I need to write a java program in which I pass the individual name (such as Lion) and get all object/data properties values related to this individual from owl (such as: Golden from has_color object property, etc.).
I wrote a java class based on the easiest
api uk.ac.ebi.brain.core.Brain, but this API doesn't have methods to list individual information.
So is there any other simple ways to fill-full my needs?
In the following example, I'm retrieving the subclasses of the main class Animal
and I don't know how to retrieve the individuals of each subclass or even by pass the name of individual to get the object/data properties of this individual:
import edu.stanford.smi.protegex.owl.model.OWLIndividual;
import java.util.List;
import java.util.Set;
import org.semanticweb.owlapi.model.OWLAnonymousIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import uk.ac.ebi.brain.core.Brain;
import uk.ac.ebi.brain.error.BrainException;
public class AnimalIndividuals {
public static void main(String[] args) throws BrainException {
Brain brain = new Brain();
brain.learn("d:\\Animal.owl");
List<String> subClasses = brain.getSubClasses("Animal", false);
for (String subClass : subClasses) {
System.out.println("Subclass of Animal: " + subClass);
}
brain.sleep();
}
}
It is currently (March 2013) not possible to retrieve the individuals attached to an object property using Brain because it is not yet implemented by ELK, the underlying reasoner in charge of this operation.
However, it seems to me that you can play around your modelling. You could assert Lion as a class
rather than as individual. You could further define the concept Lion as a subclass of has_color some Golden. It means in OWL that all the lions are golden, but things can be golden without necessarily being lions.
Implementation with Brain:
public static void main(String[] args) throws BrainException {
Brain brain = new Brain();
//Declaration of the concepts and properties
brain.addClass("Animal");
brain.addClass("Lion");
brain.addClass("Golden");
brain.addObjectProperty("has_color");
//Axioms assertion
brain.subClassOf("Lion", "Animal");
brain.subClassOf("Lion", "has_color some Golden");
//Query
List<String> results = brain.getSubClasses("has_color some Golden", false);
System.out.println(results);
brain.sleep();
}
I would like to know if it is possible to have snakeyaml load a yaml document into a javabean and if it is unable to find a match for the entry in the document as a javabean property it will place it into a generic map within the javabean...
Ex.
public class Person {
private String firstName;
private String lastName;
private Map<String, Object> anythingElse;
//Getters and setters...
}
If I load a document that looks like:
firstName: joe
lastName: smith
age: 30
Since age is not a property in the bean I would like {age, 30} to be added to the anythingElse map.
Possible?
Thanks.
No it wouldn't be possible.
From my experience and attempts it doesn't work. If you would want to load a file into a object than all attributes in that objectclass would have to have a getter and setter (I.E. the class have to be JavaBean, see Wikipedia).
I used your Person Class (See the wiki page for a proper JavaBeanClass) and this code: http://codepaste.net/dbtzqb
My error message was: "Line 3, column 4: Unable to find property 'age' on class: Person" thus proving that this simple program cannot have "unexpected" attributes. This is my fast and easy conclusion. I've not tried extensively so it may be possible but I don't know such a way (you'll have to bypass the readingmethods and JavaBean). I've used YamlBeans (https://code.google.com/p/yamlbeans/) so it's a little different but I find it easier and working ;]
Hope it's helping!
Edit
Sorry for bumping this, better late than never! I didn't see the postdate until after I posted my answer.. But hopefully it would help others seeking help assweel :3
I haven't tried the following (semi-kludgy hack) using SnakeYaml, but I have it working using YamlBeans:
Basically the idea is to define a class that extends one of the concrete implementations of java.util.Map. Then define getters that pick out distinct values and a general getter that returns everything else:
public class Person extends HashMap<String, Object>
{
public String getFirstName()
{
return (String)this.get("firstName");
}
public String getLastName()
{
return (String)this.get("lastName");
}
public Map<String, Object> getExtensions()
{
Map<String, Object> retVal = (Map<String, Object>)this.clone();
retVal.remove("firstName");
retVal.remove("lastName");
return retVal;
}
}
I'm not sure how either SnakeYaml or YamlBeans prioritizes the different type information you see when introspecting on this class, but YamlBeans (at least) is content to deserialize info into this class as if it were any other Map and doesn't seem to get confused by the addition getters (i.e. doesn't trip up on the "getExtensions").
It is possible:
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
public class YamlReader {
public static <T> T readYaml(InputStream is, Class<T> clazz){
Representer representer = new Representer();
// Set null for missing values in the yaml
representer.getPropertyUtils().setSkipMissingProperties(true);
Yaml yaml = new Yaml(representer);
T data = yaml.loadAs(is, clazz);
return data;
}
}
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.