How to get fields names of a class inside another class? - java

We are creating CSV files from DTO classes, using reflection to get the names of all the column names. ClassName.class.getDeclaredFields() gives us array of all column names.
We have a use case where, we have classes with composition relation, like below:
public class Book {
private String title;
private Author author;
}
public class Author {
private String name;
private Integer age:
}
Here Book is the root class, i.e., final CSV will be named books.csv
With Book.class.getDeclaredFields(), we only get field names title and author, but we also want field names from Author class (name and age), is there a way to get those?

Notes:
It is far from optimal.
Circular reference problem is not considered.
Given your model it would return a list with this elements:
title
author.name
author.age
Usage:
ClassMetadataService cms = new ClassMetadataService();
List<String> properties = cms.getProperties(Book.class);
Class definition:
package io.metadata;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ClassMetadataService {
public List<String> getProperties(Class clazz) {
List<String> properties = new ArrayList<>();
this.collectProperties(new ArrayList<>(), clazz, properties);
return properties;
}
private void collectProperties(List<String> pathNodes, Class clazz, List<String> properties) {
for (Field field : clazz.getDeclaredFields()) {
List<String> localPathNodes = new ArrayList<>(pathNodes);
localPathNodes.add(field.getName());
if (clazz.isPrimitive() || isJavaClass(field.getType())) {
properties.add(localPathNodes.stream().collect(Collectors.joining(".")));
} else {
collectProperties(localPathNodes, field.getType(), properties);
}
}
}
private Boolean isJavaClass(Class clazz) {
List<Class> javaClass = Arrays.asList(
// ....
Boolean.class,
Byte.class,
Short.class,
Integer.class,
Long.class,
Float.class,
Double.class,
BigDecimal.class,
BigInteger.class,
Character.class,
String.class,
LocalDate.class,
LocalDateTime.class
// ....
);
return javaClass.stream().anyMatch(jc -> jc.equals(clazz));
}
}

Related

CQEngine In Clause with MultiValueNullableAttribute

I have a class Object1 which has a list of longs called tags. I have another list of longs called tagsToSearch. How can I construct a query using CQEngine that is the following:
Select * from Object1 Where tags in (tagsToSearch)
If anyone knows how this would look using CQEngine please let me know.
This should do the trick:
package com.googlecode.cqengine;
import com.googlecode.cqengine.attribute.*;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.query.parser.sql.SQLParser;
import java.util.*;
import static com.googlecode.cqengine.codegen.AttributeBytecodeGenerator.*;
import static com.googlecode.cqengine.query.QueryFactory.*;
import static java.util.Arrays.asList;
public class TagsExample {
static class MyObject {
final String name;
final List<Long> tags;
public MyObject(String name, List<Long> tags) {
this.name = name;
this.tags = tags;
}
static final Attribute<MyObject, Long> TAGS = new MultiValueAttribute<MyObject, Long>("tags") {
public Iterable<Long> getValues(MyObject object, QueryOptions queryOptions) { return object.tags; }
};
}
public static void main(String[] args) {
IndexedCollection<MyObject> collection = new ConcurrentIndexedCollection<>();
collection.add(new MyObject("foo", asList(1L, 2L, 3L)));
collection.add(new MyObject("bar", asList(4L, 5L, 6L)));
collection.add(new MyObject("baz", asList(7L, 8L, 9L)));
// Search via a programmatic query...
Query<MyObject> nativeQuery = in(MyObject.TAGS, asList(3L, 9L));
collection.retrieve(nativeQuery)
.forEach(object -> System.out.println(object.name));
// ..prints: foo, baz
// Search via an SQL query...
String sqlQuery = "SELECT * FROM collection WHERE tags IN (3, 9)";
SQLParser<MyObject> parser = SQLParser.forPojoWithAttributes(MyObject.class, createAttributes(MyObject.class));
parser.retrieve(collection, sqlQuery)
.forEach(object -> System.out.println(object.name));
// ..prints: foo, baz
}
}

How do you create a SimpleBeanInfo in the year 2016?

I'd like to create a property editor for my Java Beans. For that I need a class that implements BeanInfo. The problem: I really don't feel comfortable with declaring an attribute name as String like this (floodColor, fillColor, percent):
import java.beans.*;
public class BarChartBeanBeanInfo extends SimpleBeanInfo
{
private final static Class myClass = BarChartBean.class;
public PropertyDescriptor[] getPropertyDescriptors()
{
try {
PropertyDescriptor flc = new PropertyDescriptor("floodColor", myClass);
PropertyDescriptor fic = new PropertyDescriptor("fillColor", myClass);
PropertyDescriptor pct = new PropertyDescriptor("percent", myClass);
PropertyDescriptor[] list = { flc, fic, pct };
return list;
}
catch (IntrospectionException iexErr)
{
throw new Error(iexErr.toString());
}
}
};
I got this from an example article about how to create a custom property editor: The trick to controlling bean customization. The article is from the year 1997.
How do you create a property editor in the year 2016 without using string declarations of variables which obviously will lead to runtime exceptions once someone changes the variable name?
I mean other than to use the Introspector. Is there e. g. some kind of annotation support for attribute names of classes?
Thank you very much for the expertise!
I tried with custom annotations, it seems to work. At least it's typesafe now and coupled to the fields.
Code
ExampleBean.java
import annotations.Descriptor;
import annotations.Property;
#Descriptor(displayName = "Example Bean", shortDescription = "This is an example bean")
public class ExampleBean {
#Property(displayName = "Integer Value", shortDescription = "This is an integer value")
int integerValue;
#Property(displayName = "Double Value", shortDescription = "This is a double value")
double doubleValue;
#Property(displayName = "String Value", shortDescription = "This is a string value")
String stringValue;
public int getIntegerValue() {
return integerValue;
}
public void setIntegerValue(int integerValue) {
this.integerValue = integerValue;
}
public double getDoubleValue() {
return doubleValue;
}
public void setDoubleValue(double doubleValue) {
this.doubleValue = doubleValue;
}
public String getStringValue() {
return stringValue;
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
}
Descriptor.java
package annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Descriptor {
public String displayName() default "";
public String shortDescription() default "";
}
Property.java
package annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.METHOD })
public #interface Property {
public String displayName() default "";
public String shortDescription() default "";
}
ExampleBeanBeanInfo.java
import java.beans.BeanDescriptor;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import annotations.Descriptor;
import annotations.Property;
public class ExampleBeanBeanInfo extends SimpleBeanInfo {
private final static Class<ExampleBean> myClass = ExampleBean.class;
public PropertyDescriptor[] getPropertyDescriptors() {
List<PropertyDescriptor> propertyDescriptors = new ArrayList<>();
try {
for (Field field : myClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Property.class)) {
Annotation annotation = field.getAnnotation(Property.class);
Property property = (Property) annotation;
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), myClass);
propertyDescriptor.setDisplayName(property.displayName());
propertyDescriptor.setShortDescription(property.shortDescription());
propertyDescriptors.add(propertyDescriptor);
}
}
return propertyDescriptors.toArray(new PropertyDescriptor[propertyDescriptors.size()]);
} catch (Exception iexErr) {
throw new Error(iexErr.toString());
}
}
public BeanDescriptor getBeanDescriptor() {
BeanDescriptor desc = new BeanDescriptor(myClass);
if (myClass.isAnnotationPresent(Descriptor.class)) {
Annotation annotation = myClass.getAnnotation(Descriptor.class);
Descriptor descriptor = (Descriptor) annotation;
desc.setDisplayName(descriptor.displayName());
desc.setShortDescription(descriptor.shortDescription());
}
return desc;
}
}
Main.java
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class Main {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(ExampleBean.class);
BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
System.out.printf( "Bean display name = '%s', description = '%s'\n", beanDescriptor.getDisplayName(), beanDescriptor.getShortDescription());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
String propertyName = propertyDescriptor.getName();
System.out.printf("Property field name = '%s', display name = '%s', description = '%s'\n", propertyName, propertyDescriptor.getDisplayName(), propertyDescriptor.getShortDescription());
}
System.exit(0);
;
}
}
Console output:
Bean display name = 'Example Bean', description = 'This is an example bean'
Property field name = 'doubleValue', display name = 'Double Value', description = 'This is a double value'
Property field name = 'integerValue', display name = 'Integer Value', description = 'This is an integer value'
Property field name = 'stringValue', display name = 'String Value', description = 'This is a string value'
This example exposes the displayname and shortdescription methods, one would have to add others of the bean and property descriptor.
If anyone has a better way, please let me know.

Spring oxm marshalling and unmarshalling a list of objects

We are using Spring for a project, and I need to marshall and unmarshall lists of objects
from and to XML.
The business objects themselves do not have #XmlRootElement annotations on them for various reasons, which cannot change.
I can do this easily with javax.xml by using a wrapper class. I'm using a simple testObject with a numeric ID and a description field:
/**
* testObject for xml convert
*
*/
package xmlconvert;
import java.io.File;
import java.io.IOException;
import java.util.List;
//import javax.xml.bind.JAXBContext;
//import javax.xml.bind.JAXBException;
//import javax.xml.bind.Marshaller;
//import javax.xml.bind.Unmarshaller;
//import javax.xml.bind.annotation.XmlRootElement;
//import javax.xml.bind.annotation.XmlType;
public class testObject
{
private static final long serialVersionUID = 1L;
public testObject()
{
setObjectid( 0L );
setDescription( " " );
}
public testObject( Long oid, String desc )
{
setObjectid( oid );
setDescription( desc );
}
private Long objectid;
public Long getObjectid()
{
return this.objectid;
}
public void setObjectid( Long oid )
{
this.objectid = oid;
}
private String description;
public String getDescription()
{
return this.description;
}
public void setDescription( String d )
{
this.description = d;
}
}
My wrapper class looks like this:
/**
* JAXBToList
*
* An object that holds the list of objects to convert.
*
*/
package xmlconvert;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import xmlconvert.testObject;
#XmlRootElement(name="testObjects")
public class JAXBToList
{
protected List<testObject> list;
public JAXBToList()
{
list = new ArrayList<testObject>();
}
public JAXBToList( List<testObject> list )
{
this.list = list;
}
#XmlElement(name="testObject")
public List<testObject> getList()
{
return (List<testObject>) this.list;
}
}
When I convert an ArrayList of these objects to XML, the code looks like this:
#XmlElements({
#XmlElement(name="testObject",
type=testObject.class)
})
public static void convertListToXML( JAXBToList objectList,
String xmlFilePath )
throws IOException, JAXBException {
JAXBContext jc = JAXBContext.newInstance( JAXBToList.class );
marshaller = jc.createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
marshaller.marshal( objectList,
new StreamResult( new FileOutputStream(xmlFilePath) ) );
}
I would like to use Spring marshaller and unmarshaller to do this, but there seems to be no way to set the context to use the JAXBToList class in the oxm class Jaxb2Marshaller, or the other varieties of marshallers.
I know there is a set Jaxb Context Properties method, but there are no instructions on what I would need to set, or if using this would do the same thing as setting the jaxb Context as shown above.
Does anyone have a link that illustrates how oxm may be used for a list of objects? A google search does not turn up much.

JSON to Java: How to model lists of objects into generic object class whose object name from json is variable

Suppose I have the following JSON that I'm unable to alter as it's provided over a web feed by someone else. I want to parse this using Jackson to Java objects.
{
"2002": [
{
"d": "description",
"t": "title"
}
],
"2003": [
{
"d": "description",
"t": "title"
}
]
}
The data represents, say, a list of TV programmes with ids=2002, 2003 ...etc, and each programme has a description and title. I want to parse this data into a List of generic Programme classes, where each programme class has fields d and t. I don't want to have separate classes for 2002, 2003 etc objects. Bearing in mind, the 2002, 2003 etc ids are not known until runtime and can evolve over time, and could also be quite a long list of possible values.
Is it possible to model this as a list of generic programmes whose id field is equal to the name of the object name from the json string? In other words, I don't want this:
public class AllProgrammes {
private List<com.example._2002> _2002;
private List<com.example._2003> _2003;
// getters and setters
}
but instead this should just contain List<Programmes>, and each programme object should have an id = 2002, or 2003, or whatever id it is.
Thanks.
If you can use Google Gson, you can do that this way:
Program.class
public class Program {
private String id;
private String title;
private String description;
public Program(String id, String title, String description) {
this.id = id;
this.title = title;
this.description = description;
}
#Override
public String toString() {
return String.format("Program[id=%s, title=%s, description=%s]", this.id, this.title, this.description);
}
}
ProgramsDeserializer.class
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
class ProgramsDeserializer implements JsonDeserializer<List<Program>> {
#Override
public List<Program> deserialize(JsonElement e, Type type, JsonDeserializationContext jdc) throws JsonParseException {
List<Program> programs = new ArrayList<>(10);
JsonObject root = e.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : root.entrySet()) {
String id = entry.getKey();
String title = "";
String description = "";
JsonElement arrayElement = entry.getValue();
if (arrayElement.isJsonArray()) {
JsonArray array = arrayElement.getAsJsonArray();
JsonElement objectElement = array.get(0);
if (objectElement.isJsonObject()) {
JsonObject object = objectElement.getAsJsonObject();
title = object.get("t").getAsString();
description = object.get("d").getAsString();
}
}
programs.add(new Program(id, title, description));
}
return programs;
}
}
GsonExample.class
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GsonExample {
private static final Logger logger = Logger.getLogger(GsonExample.class.getName());
private static final String JSON =
"{"
+ "\"2002\": ["
+ "{"
+ "\"d\": \"description\","
+ "\"t\": \"title\""
+ "}"
+ "],"
+ "\"2003\": ["
+ "{"
+ "\"d\": \"description\","
+ "\"t\": \"title\""
+ "}"
+ "]"
+ "}";
public static void main(String[] args) {
GsonExample e = new GsonExample();
e.run();
}
private void run() {
GsonBuilder builder = new GsonBuilder();
Type type = new TypeToken<List<Program>>(){}.getType();
builder.registerTypeAdapter(type, new ProgramsDeserializer());
Gson gson = builder.create();
List<Program> programs = gson.fromJson(JSON, type);
logger.log(Level.INFO, "{0}", programs);
}
}

map-like serialization w/ data values as keys

I need to serialize this:
List<Event>
where the Event class is:
public class Event {
public int id;
public String foo;
public String bar;
}
into JSON of this form:
{
"123":{"foo":"...","bar":"..."},
"345":{"foo":"...","bar":"..."}
}
Taking the "id" property out of Event and storing a Map would do the trick, but I need to support duplicate IDs.
Is there an annotation I can put on the "id" property to cause Jackson to treat it as a key, with the rest of the object as the associated value?
With your current structure of ID as the key, I'm not sure having duplicate IDs is possible in the JSON spec. Maybe if you had arrays with the IDs. I think you need to re-evaluate your desired JSON output.
You could use IdentityHashMap, so you could use different instances of string containing same value and have this result:
{"1":{"foo":"foo1","bar":"bar"},"2":{"foo":"foo2.1","bar":"bar"},"3":{"foo":"foo2","bar":"baz"},"2":{"foo":"foo2","bar":"baz"}}
that you can have executing this:
import java.io.IOException;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonTest {
public static void main(final String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper om = new ObjectMapper();
IdentityHashMap<String, Event> ihm = new IdentityHashMap<String, Event>();
List<Event> list = Arrays.asList( //
new Event(1, "foo1", "bar"), //
new Event(2, "foo2", "baz"), //
new Event(2, "foo2.1", "bar"), //
new Event(3, "foo2", "baz") //
);
for (Event e : list) {
ihm.put(String.valueOf(e.id), e);
}
System.out.println(om.writeValueAsString(ihm));
}
#JsonIgnoreProperties({ "id" })
public static class Event {
public int id;
public String foo;
public String bar;
public Event(final int id, final String foo, final String bar) {
super();
this.id = id;
this.foo = foo;
this.bar = bar;
}
}
}

Categories