How to compare two objects and get the changed fields - java

In here im logging the changes that has been done to a particular Object record. So im comparing the old record and the updated record to log the updated fields as a String. Any idea how can I do this?

Well i found a solution as below :
private static List<String> getDifference(Object s1, Object s2) throws IllegalAccessException {
List<String> values = new ArrayList<>();
for (Field field : s1.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value1 = field.get(s1);
Object value2 = field.get(s2);
if (value1 != null && value2 != null) {
if (!Objects.equals(value1, value2)) {
values.add(String.valueOf(field.getName()+": "+value1+" -> "+value2));
}
}
}
return values;
}

You may use javers library for this.
<groupId>org.javers</groupId>
<artifactId>javers-core</artifactId>
POJO:
public class Person {
private Integer id;
private String name;
// standard getters/constructors
}
Usage:
#Test
public void givenPersonObject_whenApplyModificationOnIt_thenShouldDetectChange() {
// given
Javers javers = JaversBuilder.javers().build();
Person person = new Person(1, "Michael Program");
Person personAfterModification = new Person(1, "Michael Java");
// when
Diff diff = javers.compare(person, personAfterModification);
// then
ValueChange change = diff.getChangesByType(ValueChange.class).get(0);
assertThat(diff.getChanges()).hasSize(1);
assertThat(change.getPropertyName()).isEqualTo("name");
assertThat(change.getLeft()).isEqualTo("Michael Program");
assertThat(change.getRight()).isEqualTo("Michael Java");
}
Plus other use cases are supported as well.

maybe this method can help you to solve you problem
/**
* get o1 and o2 different value of field name
* #param o1 source
* #param o2 target
* #return
* #throws IllegalAccessException
*/
public static List<String> getDiffName(Object o1,Object o2) throws IllegalAccessException {
//require o1 and o2 is not null
if (o1==null&&o2==null){
return Collections.emptyList();
}
//if only one has null
if (o1 == null){
return getAllFiledName(o2);
}
if (o2 == null){
return getAllFiledName(o1);
}
//source field
Field[] fields=o1.getClass().getDeclaredFields();
List<String> fieldList=new ArrayList<>(fields.length);
//if class is same using this to call
if (o1.getClass().equals(o2.getClass())){
//loop field to equals the field
for (Field field : fields) {
//to set the field access
field.setAccessible(true);
Object source = field.get(o1);
Object target = field.get(o2);
//using jdk8 equals to compare two objects
if (!Objects.equals(source, target)){
fieldList.add(field.getName());
}
}
}else {
//maybe o1 class is not same as o2 class
Field[] targetFields=o2.getClass().getDeclaredFields();
List<String> sameFieldNameList=new ArrayList<>();
//loop o1 field
for (Field field : fields) {
String name = field.getName();
//loop target field to get same field
for (Field targetField : targetFields) {
//if name is equal to compare
if (targetField.getName().equals(name)){
//add same field to list
sameFieldNameList.add(name);
//set access
field.setAccessible(true);
Object source = field.get(o1);
//set target access
targetField.setAccessible(true);
Object target = targetField.get(o2);
//equals
if (!Objects.equals(source, target)){
fieldList.add(field.getName());
}
}
}
}
//after loop add different source
for (Field targetField : targetFields) {
//add not same field
if (!sameFieldNameList.contains(targetField.getName())){
fieldList.add(targetField.getName());
}
}
}
return fieldList;
}
/**
* getAllFiledName
* #param obj
* #return
*/
private static List<String> getAllFiledName(Object obj) {
Field[] declaredFields = obj.getClass().getDeclaredFields();
List<String> list=new ArrayList<>(declaredFields.length);
for (Field field : declaredFields) {
list.add(field.getName());
}
return list;
}
this method can compare two object which have the same name field ,if they don't have same field,return all field Name

Kotlin generic function with reflection
It's not a perfect answer to this question, but maybe someone could use it.
data class Difference(val old: Any?, val new: Any?)
fun findDifferencesInObjects(
old: Any,
new: Any,
propertyPath: String? = null
): MutableMap<String, Difference> {
val differences = mutableMapOf<String, Difference>()
if (old::class != new::class) return differences
#Suppress("UNCHECKED_CAST")
old::class.memberProperties.map { property -> property as KProperty1<Any, *>
val newPropertyPath = propertyPath?.plus('.')?.plus(property.name) ?: property.name
property.isAccessible = true
val oldValue = property.get(old)
val newValue = property.get(new)
if (oldValue == null && newValue == null) return#map
if (oldValue == null || newValue == null) {
differences[newPropertyPath] = Difference(oldValue, newValue)
return#map
}
if (oldValue::class.isData || newValue::class.isData) {
differences.putAll(findDifferencesInObject(oldValue, newValue, newPropertyPath))
} else if (!Objects.equals(oldValue, newValue)) {
differences[newPropertyPath] = Difference(oldValue, newValue)
}
}
return differences
}
Result
{
"some.nested.and.changed.field": {
"old": "this value is old",
"new": "this value is new"
},
...
}

Related

Kafka Connect API error when set default value of STRUCT type

I want to set a default value of STRUCT, the code is shown below:
SchemaBuilder schemaBuilder = SchemaBuilder.struct().name("homeAddress")
.field("province", SchemaBuilder.STRING_SCHEMA)
.field("city", SchemaBuilder.STRING_SCHEMA);
Struct defaultValue = new Struct(schemaBuilder.build())
.put("province", "aaaa")
.put("city", "bbbb");
Schema dataSchema = SchemaBuilder.struct().name("personMessage")
.field("address", schemaBuilder.defaultValue(defaultValue).build()).build();
Struct struct = new Struct(dataSchema);
but I got error as below
Exception in thread "main" org.apache.kafka.connect.errors.SchemaBuilderException: Invalid default value
at org.apache.kafka.connect.data.SchemaBuilder.defaultValue(SchemaBuilder.java:131)
Caused by: org.apache.kafka.connect.errors.DataException: Struct schemas do not match.
at org.apache.kafka.connect.data.ConnectSchema.validateValue(ConnectSchema.java:251)
at org.apache.kafka.connect.data.ConnectSchema.validateValue(ConnectSchema.java:213)
at org.apache.kafka.connect.data.SchemaBuilder.defaultValue(SchemaBuilder.java:129)
... 1 more
I digged code of ConnectSchema.validateValue and found why throws exception,
type of schema of value is ConnectSchema, but another is SchemaBuilder, then throw exception.
case STRUCT:
Struct struct = (Struct) value;
if (!struct.schema().equals(schema))
throw new DataException("Struct schemas do not match.");
struct.validate();
method of equals
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConnectSchema schema = (ConnectSchema) o;
return Objects.equals(optional, schema.optional) &&
Objects.equals(version, schema.version) &&
Objects.equals(name, schema.name) &&
Objects.equals(doc, schema.doc) &&
Objects.equals(type, schema.type) &&
Objects.deepEquals(defaultValue, schema.defaultValue) &&
Objects.equals(fields, schema.fields) &&
Objects.equals(keySchema, schema.keySchema) &&
Objects.equals(valueSchema, schema.valueSchema) &&
Objects.equals(parameters, schema.parameters);
Can anyone help how to set default value of Type STRUCT
below is code of method 'defaultValue':
public SchemaBuilder defaultValue(Object value) {
checkCanSet(DEFAULT_FIELD, defaultValue, value);
checkNotNull(TYPE_FIELD, type, DEFAULT_FIELD);
try {
ConnectSchema.validateValue(this, value);
} catch (DataException e) {
throw new SchemaBuilderException("Invalid default value", e);
}
defaultValue = value;
return this;
}
seems will be ok if i change ConnectSchema.validateValue(this, value) to ConnectSchema.validateValue(this.builder(), value), i dont know if it's ok for other case.
Thanks.
Change this
Struct defaultValue = new Struct(schemaBuilder.build()) to
Struct defaultValue = new Struct(schemaBuilder)
ConnectSchema and SchemaBuilder both class are implementing Schema schemaBuilder.schemaBuilder.build() is passing all values to ConnectSchema constructor and return new Schema object and this object is immutable.
Look at this.
System.out.println(struct); //empty
System.out.println(struct.get("address")); //default values
System.out.println( ((Struct)struct.get("address")).getString("city") ); //default values
System.out.println( ((Struct)struct.get("address")).getString("province") ); //default values
Default values are not rewrited to struct fields. They exist only in schema definition and if filed is empty the default value is returned from schema definition.
From Struct.class
public Object get(Field field) {
Object val = this.values[field.index()];
if (val == null && field.schema().defaultValue() != null) {
val = field.schema().defaultValue();
}
return val;
}

Translate Object into Datastore Entity [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
My goal here is to make a very simplistic Object<->Entity mapping here is what I have done so far, this is recursive:
/**
*
* Create Entity objects that can be persisted into the GAE datastore,
* including its Parent-Child relationships (if necessary).
*
* #param parent parent of the generated Entity or Entities
* #param instance
* #return
*/
public Entity createEntityFrom(Key parent, Object instance){
Preconditions.checkNotNull(instance, "Object should not be null");
if (stack == null){ // List<Entity> stack;
stack = new LinkedList<Entity>();
}
stack.clear();
Key key = createKeyFrom(parent, instance); // inspect kind and create key
Map<String,Object> props = new LinkedHashMap<String, Object>();
List<Entity> target = null;
Entity e = new Entity(key);
Field[] fields = instance.getClass().getDeclaredFields();
for (Field field : fields) {
if (target == null){
target = new LinkedList<Entity>();
}
String fieldName = field.getName();
if(field.isAnnotationPresent(Id.class)){
// skip
continue;
}
try {
boolean isAccessible = field.isAccessible();
field.setAccessible(true);
Class<?> clazz = field.getType();
Object fieldValue = field.get(instance);
if (fieldValue == null){
e.setProperty(fieldName, null);
} else if (fieldValue instanceof String) {
setProperty(e, fieldName, fieldValue);
} else if(fieldValue instanceof Number
|| fieldValue instanceof Long
|| fieldValue instanceof Integer
|| fieldValue instanceof Short) {
setProperty(e, fieldName, fieldValue);
} else if(fieldValue instanceof Boolean) {
setProperty(e, fieldName, fieldValue);
} else if(fieldValue instanceof Date) {
setProperty(e, fieldName, fieldValue);
} else if(fieldValue instanceof User) { // GAE support this type
setProperty(e, fieldName, fieldValue);
} else if(fieldValue instanceof List) {
LOG.debug( "Processing List valueType");
if (field.isAnnotationPresent(Embedded.class)){
setProperty(e, fieldName, createEmbeddedEntityFromList(parent, (List) fieldValue));
} else {
// TODO
List<Object> list = (List<Object>) fieldValue;
List<Entity> result = new LinkedList<Entity>();
for (Object o : list){
//result = createEntityFrom(e.getKey(), o);
}
}
} else if(fieldValue instanceof Map){
LOG.debug( "Processing Map valueType");
if (field.isAnnotationPresent(Embedded.class)){
setProperty(e, fieldName, createEmbeddedEntityFromMap(parent, (Map) fieldValue));
} else {
Entity mapEntity = new Entity(KeyStructure.createKey(e.getKey(), "kind", "key"));
Map map = (Map) fieldValue;
Iterator it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry entry = (Map.Entry) it.next();
Object entryKey = entry.getKey();
Object entryVal = entry.getValue();
validateKey(entryKey);
if(entryKey instanceof String){
//createEntityFrom()
} else if (entryKey instanceof Long){
} else {
}
}
}
} else {
// For primitives
if (clazz.equals(int.class)){
int i = (Integer) fieldValue;
setProperty(e, fieldName, i);
} else if (clazz.equals(boolean.class)){
boolean i = (Boolean) fieldValue;
setProperty(e, fieldName, i);
} else if (clazz.equals(byte.class)){
byte i = (Byte) fieldValue;
setProperty(e, fieldName, i);
} else if (clazz.equals(short.class)){
short i = (Short) fieldValue;
setProperty(e, fieldName, i);
} else if (clazz.equals(long.class)){
long i = (Long) fieldValue;
setProperty(e, fieldName, i);
} else if (clazz.equals(float.class)){
float i = (Float) fieldValue;
setProperty(e, fieldName, i);
} else if (clazz.equals(double.class)){
double i = (Double) fieldValue;
setProperty(e, fieldName, i);
} else { // POJO
if (field.isAnnotationPresent(Parent.class)){
// update key
Key oldKey = e.getKey();
Entity parentEntity = createEntityFrom(null, field); // then add to the stack
// TODO
} else if (field.isAnnotationPresent(Child.class)){
Entity childEntity = createEntityFrom(e.getKey(), field);
setProperty(e, fieldName, childEntity.getKey());
}
Key parentKey = e.getKey();
Entity child = createEntityFrom(parentKey, fieldValue);
setProperty(e, fieldName, child.getKey());
stack.add(e);
}
}
field.setAccessible(isAccessible);
} catch(IllegalAccessException ex){
ex.printStackTrace();
}
}
return e;
}
Test
Here's how it its used:
#Test
public void test(){
Friend f = new Friend(); // one Entity
f.setName("TestUser");
f.setAge(25);
f.setNewAddress(new Address("Test City")); // one Entity, #Child
f.setOldAddress(new Address("Old Test City")); // not included, #Embedded
List<Entity> stack = new LinkedList<Entity>();
ObjectMapper mapper = new ObjectMapper();
mapper.createEntityFrom(null, f);
assertEquals(2, stack.size());
}
Question is there any existing Object-to-Entity mapping code? I'm not really looking for full pledged ORM framework, just a simple POJO to Entity converter code. And is this code bloated in any way?
You're on the painful way of writing yet another persistence library. Be careful, a "pure" object model hardly matches with datastore entities. At some point you're going to make compromises to match the underlying entity structure, and at that time you will realize that a better library was doing just that all along.
For example : you're providing your entity's parent key, but where is the key of that entity if you just want to update it? How will you handle lazy loading, if at some point you don't want to load all your object graph for one small query? How do you serialize keys? What about Blobs and byte[] ? How do you handle transcient attributes ?
Do yourself a favor before you spend weeks of work on something that already exists, use a proven persistence tool before you stumble on a problem that is too big for one man. If you need to keep your pure object model (for example if you're using GWT on the front end), then map persistent classes to your POJOs.
And if you really don't need something like, say, Objectify, it probably means you'll be better off with the flexibility of the low level API. It does not take that long to map entities to your classes.
The bottom line is that by writing your own "framework", you're missing out on both the flexibility of the manual-&-boring low level API and the rigid-but-solid tool that does it all better than you.
This is the GaeMarshaller that worked for me.

Is there a common Java method to trim every string in an object graph?

I'm hoping to trim all Strings that are part of an object graph.
So I have an object graph like so
RootElement
- name (String)
- adjective (String)
- items ArrayOfItems
- getItems (List<Item>)
- get(i) (Item)
Item
- name (String)
- value (double)
- alias (String)
- references ArrayOfReferences
- getReferences (List<Reference>)
- get(i) (Reference)
Reference
- prop1 (String)
- prop2 (Integer)
- prop3 (String)
There is a get and set pair for every property of every class represented in this object graph. Ideally every field of type String would end up trimmed, including enumerating any child objects contained in collections. There are no cycles contained within the object graph.
Is there any java library that implements some sort of generic object graph visitor pattern or String\Reflection utility library that does this?
An external third party library that does this would also be fine, it does not have to be part of the standard java libraries.
No, there's no built-in traversal for something like this, and remember that Java Strings are immutable, so you can't actually trim in place--you have to trim and replace. Some objects may not permit modification of their String variables.
Below is the explanation of solution that I have built using Java Reflection API. I have posted the working code (with its url to github) below. This solution mainly uses:
Java Reflection API
Independent handling of Java Collections
Recursion
To start with, I have used Introspector to go over the readMethods of the Class omitting the methods defined for Object
for (PropertyDescriptor propertyDescriptor : Introspector
.getBeanInfo(c, Object.class).getPropertyDescriptors()) {
Method method = propertyDescriptor.getReadMethod();
Cases
If the current level of Property is of type String
If its an Object Array of Properties
If its a String array
If its a type of Java Collection class
Separate placement for Map with special conditions to process its keys and values
This utility uses the Java Reflection API to traverse through an object graph with disciplined syntax of getters and setters and trims all strings encountered within an Object graph recursively.
Code
This entire util class with the main test class (and custom data types/pojos) is here on my github
Usage:
myObj = (MyObject) SpaceUtil.trimReflective(myObj);
Util method:
public static Object trimReflective(Object object) throws Exception {
if (object == null)
return null;
Class<? extends Object> c = object.getClass();
try {
// Introspector usage to pick the getters conveniently thereby
// excluding the Object getters
for (PropertyDescriptor propertyDescriptor : Introspector
.getBeanInfo(c, Object.class).getPropertyDescriptors()) {
Method method = propertyDescriptor.getReadMethod();
String name = method.getName();
// If the current level of Property is of type String
if (method.getReturnType().equals(String.class)) {
String property = (String) method.invoke(object);
if (property != null) {
Method setter = c.getMethod("set" + name.substring(3),
new Class<?>[] { String.class });
if (setter != null)
// Setter to trim and set the trimmed String value
setter.invoke(object, property.trim());
}
}
// If an Object Array of Properties - added additional check to
// avoid getBytes returning a byte[] and process
if (method.getReturnType().isArray()
&& !method.getReturnType().isPrimitive()
&& !method.getReturnType().equals(String[].class)
&& !method.getReturnType().equals(byte[].class)) {
System.out.println(method.getReturnType());
// Type check for primitive arrays (would fail typecasting
// in case of int[], char[] etc)
if (method.invoke(object) instanceof Object[]) {
Object[] objectArray = (Object[]) method.invoke(object);
if (objectArray != null) {
for (Object obj : (Object[]) objectArray) {
// Recursively revisit with the current property
trimReflective(obj);
}
}
}
}
// If a String array
if (method.getReturnType().equals(String[].class)) {
String[] propertyArray = (String[]) method.invoke(object);
if (propertyArray != null) {
Method setter = c.getMethod("set" + name.substring(3),
new Class<?>[] { String[].class });
if (setter != null) {
String[] modifiedArray = new String[propertyArray.length];
for (int i = 0; i < propertyArray.length; i++)
if (propertyArray[i] != null)
modifiedArray[i] = propertyArray[i].trim();
// Explicit wrapping
setter.invoke(object,
new Object[] { modifiedArray });
}
}
}
// Collections start
if (Collection.class.isAssignableFrom(method.getReturnType())) {
Collection collectionProperty = (Collection) method
.invoke(object);
if (collectionProperty != null) {
for (int index = 0; index < collectionProperty.size(); index++) {
if (collectionProperty.toArray()[index] instanceof String) {
String element = (String) collectionProperty
.toArray()[index];
if (element != null) {
// Check if List was created with
// Arrays.asList (non-resizable Array)
if (collectionProperty instanceof List) {
((List) collectionProperty).set(index,
element.trim());
} else {
collectionProperty.remove(element);
collectionProperty.add(element.trim());
}
}
} else {
// Recursively revisit with the current property
trimReflective(collectionProperty.toArray()[index]);
}
}
}
}
// Separate placement for Map with special conditions to process
// keys and values
if (method.getReturnType().equals(Map.class)) {
Map mapProperty = (Map) method.invoke(object);
if (mapProperty != null) {
// Keys
for (int index = 0; index < mapProperty.keySet().size(); index++) {
if (mapProperty.keySet().toArray()[index] instanceof String) {
String element = (String) mapProperty.keySet()
.toArray()[index];
if (element != null) {
mapProperty.put(element.trim(),
mapProperty.get(element));
mapProperty.remove(element);
}
} else {
// Recursively revisit with the current property
trimReflective(mapProperty.get(index));
}
}
// Values
for (Map.Entry entry : (Set<Map.Entry>) mapProperty
.entrySet()) {
if (entry.getValue() instanceof String) {
String element = (String) entry.getValue();
if (element != null) {
entry.setValue(element.trim());
}
} else {
// Recursively revisit with the current property
trimReflective(entry.getValue());
}
}
}
} else {// Catch a custom data type as property and send through
// recursion
Object property = (Object) method.invoke(object);
if (property != null) {
trimReflective(property);
}
}
}
} catch (Exception e) {
throw new Exception("Strings cannot be trimmed because: ", e);
}
return object;
}
Test
I also have a test class in there which creates a relatively complex object. The test class has different scenarios that cover:
String properties
Properties as custom datatypes which in turn have String properties
Properties as custom datatypes which in turn have properties as custom datatypes which in turn have String properties
List of custom data types
Set of Strings
Array of custom data types
Array of Strings
Map of String and custom data type
Object Graph:
Test Object Code Snippet:
public static Music buildObj() {
Song song1 = new Song();
Song song2 = new Song();
Song song3 = new Song();
Artist artist1 = new Artist();
Artist artist2 = new Artist();
song1.setGenre("ROCK ");
song1.setSonnet("X ");
song1.setNotes("Y ");
song1.setCompostions(Arrays.asList(new String[] { "SOME X DATA ",
"SOME OTHER DATA X ", "SOME MORE DATA X ", " " }));
Set<String> instruments = new HashSet<String>();
instruments.add(" GUITAR ");
instruments.add(" SITAR ");
instruments.add(" DRUMS ");
instruments.add(" BASS ");
song1.setInstruments(instruments);
song2.setGenre("METAL ");
song2.setSonnet("A ");
song2.setNotes("B ");
song2.setCompostions(Arrays.asList(new String[] { "SOME Y DATA ",
" SOME OTHER DATA Y ",
" SOME MORE DATA Y ", " " }));
song3.setGenre("POP ");
song3.setSonnet("DONT ");
song3.setNotes("KNOW ");
song3.setCompostions(Arrays.asList(new String[] { "SOME Z DATA ",
" SOME OTHER DATA Z ",
" SOME MORE DATA Z ", " " }));
artist1.setSongList(Arrays.asList(new Song[] { song1, song3 }));
artist2.setSongList(Arrays.asList(new Song[] { song1, song2, song3 }));
Map<String, Person> artistMap = new HashMap<String, Person>();
Person tutor1 = new Person();
tutor1.setName("JOHN JACKSON DOE ");
artistMap.put(" Name ", tutor1);
Person coach1 = new Person();
coach1.setName("CARTER ");
artistMap.put("Coach ", coach1);
artist2.setTutor(artistMap);
music.setSongs(Arrays.asList(new Song[] { song1, song2, song3 }));
music.setArtists(Arrays.asList(new Artist[] { artist1, artist2 }));
music.setLanguages(new String[] { " ENGLISH ", "FRENCH ",
"HINDI " });
Person singer1 = new Person();
singer1.setName("DAVID ");
Person singer2 = new Person();
singer2.setName("JACOB ");
music.setSingers(new Person[] { singer1, singer2 });
Human man = new Human();
Person p = new Person();
p.setName(" JACK'S RAGING BULL ");
SomeGuy m = new SomeGuy();
m.setPerson(p);
man.setMan(m);
music.setHuman(man);
return music;
}
Outcome:
#######BEFORE#######
>>[>>DAVID ---<<, >>JACOB ---<<]---[ ENGLISH , FRENCH , HINDI ]---[>>ROCK ---X ---Y ---[SOME X DATA , SOME OTHER DATA X , SOME MORE DATA X , ]---[ SITAR , GUITAR , BASS , DRUMS ]<<, >>METAL ---A ---B ---[SOME Y DATA , SOME OTHER DATA Y , SOME MORE DATA Y , ]---<<, >>POP ---DONT ---KNOW ---[SOME Z DATA , SOME OTHER DATA Z , SOME MORE DATA Z , ]---<<]---[>>---[>>ROCK ---X ---Y ---[SOME X DATA , SOME OTHER DATA X , SOME MORE DATA X , ]---[ SITAR , GUITAR , BASS , DRUMS ]<<, >>POP ---DONT ---KNOW ---[SOME Z DATA , SOME OTHER DATA Z , SOME MORE DATA Z , ]---<<]<<, >>{Coach =>>CARTER ---<<, Name =>>JOHN JACKSON DOE ---<<}---[>>ROCK ---X ---Y ---[SOME X DATA , SOME OTHER DATA X , SOME MORE DATA X , ]---[ SITAR , GUITAR , BASS , DRUMS ]<<, >>METAL ---A ---B ---[SOME Y DATA , SOME OTHER DATA Y , SOME MORE DATA Y , ]---<<, >>POP ---DONT ---KNOW ---[SOME Z DATA , SOME OTHER DATA Z , SOME MORE DATA Z , ]---<<]<<]---=> JACK'S RAGING BULL <=<<
Number of spaces : 644
#######AFTER#######
>>[>>DAVID---<<, >>JACOB---<<]---[ENGLISH, FRENCH, HINDI]---[>>ROCK---X---Y---[SOME X DATA, SOME OTHER DATA X, SOME MORE DATA X, ]---[GUITAR, SITAR, DRUMS, BASS]<<, >>METAL---A---B---[SOME Y DATA, SOME OTHER DATA Y, SOME MORE DATA Y, ]---<<, >>POP---DONT---KNOW---[SOME Z DATA, SOME OTHER DATA Z, SOME MORE DATA Z, ]---<<]---[>>---[>>ROCK---X---Y---[SOME X DATA, SOME OTHER DATA X, SOME MORE DATA X, ]---[GUITAR, SITAR, DRUMS, BASS]<<, >>POP---DONT---KNOW---[SOME Z DATA, SOME OTHER DATA Z, SOME MORE DATA Z, ]---<<]<<, >>{Name=>>JOHN JACKSON DOE---<<, Coach=>>CARTER---<<}---[>>ROCK---X---Y---[SOME X DATA, SOME OTHER DATA X, SOME MORE DATA X, ]---[GUITAR, SITAR, DRUMS, BASS]<<, >>METAL---A---B---[SOME Y DATA, SOME OTHER DATA Y, SOME MORE DATA Y, ]---<<, >>POP---DONT---KNOW---[SOME Z DATA, SOME OTHER DATA Z, SOME MORE DATA Z, ]---<<]<<]---=>JACK'S RAGING BULL<=<<
Number of spaces : 111
There is a non-zero count of the number of spaces in the above trimmed output because I didn't make an effort to override toString of any collections (List, Set) or Map. There are certain improvements to the code I want to make but for your case the solution should work just fine.
Limitations (further improvements)
Cannot handle undisciplined syntax of properties (invalid getters/setters)
Cannot handle chained Collections: for example, List<List<Person>> - because of the exclusive support to disciplined getters/setters convention
No Guava collection library support
Building off #SwissArmyKnife I converted his simple String trimming function into an interface with a default method. So any object where you would like to use object.trim(), you just have to add "implements Trimmable".
Simple String trim interface: Trimmable.class
/**
* Utility interface that trims all String fields of the implementing class.
*/
public interface Trimmable {
/**
* Trim all Strings
*/
default void trim(){
for (Field field : this.getClass().getDeclaredFields()) {
try {
field.setAccessible(true);
Object value = field.get(this);
if (value != null){
if (value instanceof String){
String trimmed = (String) value;
field.set(this, trimmed.trim());
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
An object that we would like to be trimmable: Person.class (implements Trimmable interface)
public class Person implements Trimmable {
private String firstName;
private String lastName;
private int age;
// getters/setters omitted
}
Now you can use person.trim()
Person person = new Person();
person.setFirstName(" John ");
person.setLastName(" Doe");
person.setAge(30);
person.trim();
I made a simple method for trimming String values with Reflection API.
public Object trimStringValues(Object model){
for(Field field : model.getClass().getDeclaredFields()){
try{
field.setAccessible(true);
Object value = field.get(model);
String fieldName = field.getName();
if(value != null){
if(value instanceof String){
String trimmed = (String) value;
field.set(model, trimmed.trim());
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
I haven't bumped in to any problems with this one yet. I know its an old thread, but it might help somone whos is looking for something simple.

Reflection generic get field value

I am trying to obtain a field's value via reflection. The problem is I don't know the field's type and have to decide it while getting the value.
This code results with this exception:
Can not set java.lang.String field com....fieldName to java.lang.String
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Class<?> targetType = field.getType();
Object objectValue = targetType.newInstance();
Object value = field.get(objectValue);
I tried to cast, but I get compilation errors:
field.get((targetType)objectValue)
or
targetType objectValue = targetType.newInstance();
How can I do this?
Like answered before, you should use:
Object value = field.get(objectInstance);
Another way, which is sometimes prefered, is calling the getter dynamically. example code:
public static Object runGetter(Field field, BaseValidationObject o)
{
// MZ: Find the correct method
for (Method method : o.getMethods())
{
if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3)))
{
if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase()))
{
// MZ: Method found, run it
try
{
return method.invoke(o);
}
catch (IllegalAccessException e)
{
Logger.fatal("Could not determine method: " + method.getName());
}
catch (InvocationTargetException e)
{
Logger.fatal("Could not determine method: " + method.getName());
}
}
}
}
return null;
}
Also be aware that when your class inherits from another class, you need to recursively determine the Field. for instance, to fetch all Fields of a given class;
for (Class<?> c = someClass; c != null; c = c.getSuperclass())
{
Field[] fields = c.getDeclaredFields();
for (Field classField : fields)
{
result.add(classField);
}
}
You should pass the object to get method of the field, so
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Object value = field.get(object);
I use the reflections in the toString() implementation of my preference class to see the class members and values (simple and quick debugging).
The simplified code I'm using:
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
Class<?> thisClass = null;
try {
thisClass = Class.forName(this.getClass().getName());
Field[] aClassFields = thisClass.getDeclaredFields();
sb.append(this.getClass().getSimpleName() + " [ ");
for(Field f : aClassFields){
String fName = f.getName();
sb.append("(" + f.getType() + ") " + fName + " = " + f.get(this) + ", ");
}
sb.append("]");
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
I hope that it will help someone, because I also have searched.
Although it's not really clear to me what you're trying to achieve, I spotted an obvious error in your code:
Field.get() expects the object which contains the field as argument, not some (possible) value of that field. So you should have field.get(object).
Since you appear to be looking for the field value, you can obtain that as:
Object objectValue = field.get(object);
No need to instantiate the field type and create some empty/default value; or maybe there's something I missed.
Integer typeValue = 0;
try {
Class<Types> types = Types.class;
java.lang.reflect.Field field = types.getDeclaredField("Type");
field.setAccessible(true);
Object value = field.get(types);
typeValue = (Integer) value;
} catch (Exception e) {
e.printStackTrace();
}
`
//Here is the example I used for get the field name also the field value
//Hope This will help to someone
TestModel model = new TestModel ("MyDate", "MyTime", "OUT");
//Get All the fields of the class
Field[] fields = model.getClass().getDeclaredFields();
//If the field is private make the field to accessible true
fields[0].setAccessible(true);
//Get the field name
System.out.println(fields[0].getName());
//Get the field value
System.out.println(fields[0].get(model));
`
I post my solution in Kotlin, but it can work with java objects as well.
I create a function extension so any object can use this function.
fun Any.iterateOverComponents() {
val fields = this.javaClass.declaredFields
fields.forEachIndexed { i, field ->
fields[i].isAccessible = true
// get value of the fields
val value = fields[i].get(this)
// print result
Log.w("Msg", "Value of Field "
+ fields[i].name
+ " is " + value)
}}
Take a look at this webpage: https://www.geeksforgeeks.org/field-get-method-in-java-with-examples/
Was able to access private fields in a class using following method
Beneficiary ben = new Beneficiary();//class with multiple fields
ben.setName("Ashok");//is set by a setter
//then to get that value following was the code which worked for me
Field[] fields = ben.getClass().getDeclaredFields();
for(Field field: fields) {
field.setAccessible(true);//to access private fields
System.out.println(field.get(ben));//to get value
//assign value for the same field.set(ben, "Y");//to set value
}
You are calling get with the wrong argument.
It should be:
Object value = field.get(object);

Remove attributes from XMLBean

Assume there's an XMLBeans XmlObject with attributes, how can I get selected attributes in single step?
I'm expecting like something ....
removeAttributes(XmlObject obj, String[] selectableAttributes){};
Now the above method should return me the XMLObject with only those attributes.
Assumption: the attributes that you want to remove from your XmlObject must be optional in the corresponding XML Schema. Under this assumption, XMLBeans provides you with a couple of useful methods: unsetX and isSetX (where X is your attribute name. So, we can implement a removeAttributes method in this way:
public void removeAttributes(XmlObject obj,
String[] removeAttributeNames)
throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException, SecurityException,
NoSuchMethodException {
Class<?> clazz = obj.getClass();
for (int i = 0; i < removeAttributeNames.length; i++) {
String attrName =
removeAttributeNames[i].substring(0, 1).toUpperCase() +
removeAttributeNames[i].substring(1);
String isSetMethodName = "isSet" + attrName;
Boolean isSet = null;
try {
Method isSetMethod = clazz.getMethod(isSetMethodName);
isSet = (Boolean) isSetMethod.invoke(obj, new Object[] {});
} catch (NoSuchMethodException e) {
System.out.println("attribute " + removeAttributeNames[i]
+ " is not optional");
}
if (isSet != null && isSet.booleanValue() == true) {
String unsetMethodName = "unset" + attrName;
Method unsetMethod = clazz.getMethod(unsetMethodName);
unsetMethod.invoke(obj, new Object[] {});
}
}
}
Note 1: I have slightly modified the semantics of your method signature: the second argument (the String[]) is actually the list of attributes that you want to remove. I think this is more consistent with the method name (removeAttributes), and it also simplify things (using unsetX and isSetX).
Note 2: The reason for calling isSetX before calling unsetX is that unsetX would throw an InvocationTargetException if called when the attribute X is not set.
Note 3: You may want to change exception handling according to your needs.
I think you can use a cursor ... they are cumbersome to handle, but so is reflection.
public static XmlObject RemoveAllAttributes(XmlObject xo) {
return RemoveAllofType(xo, TokenType.ATTR);
}
public static XmlObject RemoveAllofTypes(XmlObject xo, final TokenType... tts) {
printTokens(xo);
final XmlCursor xc = xo.newCursor();
while (TokenType.STARTDOC == xc.currentTokenType() || TokenType.START == xc.currentTokenType()) {
xc.toNextToken();
}
while (TokenType.ENDDOC != xc.currentTokenType() && TokenType.STARTDOC != xc.prevTokenType()) {
if (ArrayUtils.contains(tts, xc.currentTokenType())) {
xc.removeXml();
continue;
}
xc.toNextToken();
}
xc.dispose();
return xo;
}
I am using this simple method to clean everything in the element. You can omit the cursor.removeXmlContents to only delete attributes. Second cursor is used to return to the initial position:
public static void clearElement(final XmlObject object)
{
final XmlCursor cursor = object.newCursor();
cursor.removeXmlContents();
final XmlCursor start = object.newCursor();
while (cursor.toFirstAttribute())
{
cursor.removeXml();
cursor.toCursor(start);
}
start.dispose();
cursor.dispose();
}

Categories

Resources