I'm using ProGuard to obfuscate my source code, but I'm running into a problem where my obfuscated code re-uses the same field name for fields with different types. For example:
public class Pojo {
int id;
String name;
}
Becomes:
public class Pojo {
int a;
String a;
}
This is causing problems when trying to use JSON serialization/deserialization, so I'd like to have unique field names instead.
I tried adding -useuniqueclassmembernames though it didn't work. Couldn't see any other options, besides maybe using the -obfuscationdictionary option, but that seems like overkill.
Related
I am trying to generate a CSV mapping for the fields of a class in Java in an automatic way since I need to use it several times.
I have the following method for trying to get the field names: (where CSV header is something like "DB_NAME|FIELD_NAME|ADDITIONAL_F1|ADDITIONAL_F2")
package util;
import java.lang.reflect.Field;
public class CsvAttributesMappingGenerator {
public static String generateCsvAttributesMapping(Class<?> model) {
StringBuilder csvBuilder = new StringBuilder();
Field[] fieldList = model.getDeclaredFields();
for (Field field : fieldList) {
//field.setAccessible(true);
csvBuilder.append(field.getName().replaceAll("(.)(\\p{Upper})", "$1_$2").toUpperCase());
csvBuilder.append("|");
csvBuilder.append(field.getName());
csvBuilder.append("||\n");
}
return formatOutput(csvBuilder.toString());
}
private static String formatOutput(String classText) {
String delimiter = "\n******************************\n";
return String.format("%s%s%s", delimiter, classText, delimiter);
}
}
and a test call like:
import objects.User;
import org.junit.Test;
import util.CsvAttributesMappingGenerator;
public class CsvAttributesMappingGeneratorTest {
#Test
public void testGenerationWithObject() {
System.out.println(CsvAttributesMappingGenerator.generateCsvAttributesMapping(User.class));
}
}
The object to be parsed has the following structure:
package objects;
public class User {
private String userName;
private String userEmail;
private int userAge;
private String otherDetails;
// getters, setters and all args constuctor here
}
The output should have multiple rows like FIELD_NAME|fieldName|| where the camel cased item should be collected from the given class. I attempted to use Java Reflection API as I have seen on several examples but I get a strange String output instead. (not the serialized #randomCharsLikeName). Tried toString() and other dirty tricks but nothing worked.
Can someone tip me up a little with this? Or at least tell me if it is possible to do what I tried?
Thanks in advance!
EDIT: the current code prototype presented in the question works in an isolated environment (separate new project) and displays the expected output. It does not work though integrated within the whole application I am trying to integrate it into. I will keep researching and let you know the root cause (in the real app I am using also lombok for the classes (
#AllArgsConstructor, #NoArgsConstructor, #Data, #ToString
), but I do not honestly think that this might be an issue while using Reflection)
I found the issue in the meanwhile. While I was play-prototyping the generator, I used:
csvBuilder.append(field.getClass().getName().replaceAll("(.)(\\p{Upper})", "$1_$2").toUpperCase());
which produced outputs like JAVA.LANG.REFLECT._FIELD|java.lang.reflect.Field||
Since I simply forgot that I actually improved it to use actual class object as a parameter to the function. Before asking the question I made some Sonar fixes to the code and did not notice that I fixed a WARN to remove the .getClass() method invocation since I already pass in a class (I thought it would not make a difference since it is just a warning). Moral tip of the day - NEVER EVER ignore warnings.
So the code snippets presented in the question work fine now in an isolated dummy project and also integrated within a more complex project, using the fixed line:
csvBuilder.append(field.getName().replaceAll("(.)(\\p{Upper})", "$1_$2").toUpperCase());
Also as someone suggested in the comments, the field.setAccessible(true); can be removed since it is useless for the purpose of the method.
My goal is to take a XML and/or XSD file and dynamically create a Java object that contains costume annotations and load it into the JVM. A 3rd party library would then look for those objects containing that annotation and perform some function to it. example output of what the java object would look like is as follows
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.PlanningVariable;
#PlanningEntity
public class NameAssignment extends ResourceAssignment{
private String name;
#PlanningVariable(valueRangeProviderRefs = { "PlannerCountRange" })
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The part i'm struggling with is generating the annotation fields #PlanningEntity and #PlanningVariable(valueRangeProviderRefs = { "PlannerCountRange" }) before, during or after the unmarshalling of the XML.
I've been trying to figure it out with JAXB, Dynamic JAXB, Dynamic JAXB, JavaAssist(for byte code manipulation), JCypher, and XJC(Just for compiling the classes). I even thought about dumping Java altogether and using Groovy.
I'm starting to feel like I'm over complicating this and need some guidance. This is a "from scratch" project so I have zero constraints on how I implement it.
Any Help would be greatly appreciated.
With jaxb2-annotate-plugin you may inject any static annotations into generated code just provide binding.
Please look for answer Custom annotation with jaxb2-annotate-plugin and XJC tool for details and examples how to use it.
How do I write a Java inner class with custom properties at compile time using annotations?
For instance, I want this :
#Generate
class Person {
String firstname, lastname;
}
to generate:
class Person {
String firstname, lastname;
public static class $Fields {
public static String firstname = "firstname";
public static String lastname = "lastname";
}
}
How can I write the interface:
#Retention(RetentionPolicy.SOURCE)
public #interface Generate {
// ...
}
I understand I need to do some kind of AST transformation to make this magical.
I am also aware of project lombok, but I want to know what the least common denominator is with a simple example, preferably within one method, and preferably something that a good editor would consider automatically, for instance RetentionPolicy.SOURCE for the javac compiler, which can be used in Intellij IDEA.
Project lombok is a beast code wise and is tough place to start.
It must be simpler than that, is it not?
Any ideas?
You can do this by reflection, but your new class won't be an inner class; but be warned, you will lose static type safety.
It can be done in 2 steps:
Read the annotated class via reflection and transform it into a String which represents the source code of your new class.
Write this string to file, compile this String using the Java compiler API and then load and instantiate the new class, all programatically; see exact steps here.
Alternatives to achieving similar functionality can also be obtained by bytecode instrumentation (see cglib or javassist) or maybe even with proxies.
I have a class Car that has a Field named trunk. How can I retrieve that name only with the property that is assigned to it and without any fixed String.
Something working like this fiction would be great:
System.out.println(new Car().getTrunk().getField().getName());
Output:
trunk
I don't want to use a fixed String to retrieve the Field and it's name because that would not refactor well. If I decide to rename from trunk to boot I want this to be handled completely by my IDE's refactoring tool.
UPDATE Car class:
public class Car{
String trunk;
// getters + setters
}
BACKGROUND:
I want to use Primefaces' Dynamic Columns for a CRUD-UI for several entities which uses a columnTemplate containig the names of the Fields/properties to be evaluated by Expression Language.
Consider introducing a enum holding all the properties (without values), for example
enum CarProperty {
TRUNK, HOOD, WHATEVER;
}
and storing them in Car as EnumMap:
class Car {
private Map<CarProperty, String> propsToValues = new EnumMap<>(...);
public String getValue(CarProperty property) { ... }
}
property name could be accessed by
((CarProperty) anyPropery).toString()
And obviously it is easy to refactor
Is not a good idea access to the property name.
With reflection you are breaking OOP principles.
getTrunk() not allways need to access a trunk property
You can have a
private Trunk trunk;//remember to change the TRUNK_FIELD
public static final TRUNK_FIELD = "trunk"
but... try to avoid this solution.
What libraries are available that can do something like
public class Person {
private Long id;
private Name;
private List<Long> associations;
// accessors
}
public class Name {
private String first;
private String last;
// accessors
}
into something like
id=1
associations=1,2,3,4,5
name.first=Franz
name.last=See
And If there's no library to do that, what's a good way to do it? :-)
I doubt there's a library for that since common way to serialize beans is into XML. You may write simple library yourself using Java Reflection API to get list of properties and extract their values. It would be more common solution than making custom toString() for any class you may need to serialize.
Well I think instead of using any external library you can do it yourself just add getters and setters in your javabeans and override to string method of it and then you can form the string as you want.Then only task remaining is to write that string into one file, thats it!!!!!
Well, you can go check how XMLEncoder extract field names and values from object, and try to rewrite it to output properties files. I think that, by replacing xml output by properties output, you can get a fairly good properties creator. Notice, as an added benefit, that the same goes for properties reading using an equivalent opf XMLDecoder.
Please check if JSON would solve this problem.
import net.sf.json.JSONObject;
public class YourJSONJavaExample
{
public static void main(String args[]){
JSONObject object=new JSONObject();
object.put("firstname","John");
object.put("age",new Integer(21));
object.put("lastname","smith");
System.out.println(object);
}
}