I have huge object, I have its class of course, I'm extracting some values from it, but since it is really big I don't know in which list or where the value I'm looking for is.
Is there a way to create some kind of object breakdown routing and search every part for the value I'm expecting, and it is hidd开发者_StackOverflowen somewhere in the object, I just can't find it in eclipse its too nested.
I thought of using reflection to go trough all fields of object class and search for the value inside of each field(fields inside lists(list of lists etc)). Any other ideas?
Unfortunately none of these answers help me, I'm starting a bounty
I assume you just want to find a specific value and to trace its source. And all this, you want to do at debug time. I would suggest two options.
Option1 Use JSON - Serialize the object to json string and do a manual text search on the result. You would neeed json.jar (or any other parser) for this.
try {
System.out.println(new JSONObject(new YourHugeObject()).toString(5));
} catch (JSONException e) {
log(e);
}
Which will produce something like this. (I have simulated this by creating an object with some nested fields,lists,maps)
{
"ct": {
"a": 1,
"b": "sdf",
"f": 12,
"nested": {
"key1": {
"kk": "kk",
"ssdf": 123
},
"onemorekey": {
"kk": "kk",
"ssdf": 123
}
}
},
"doubleProp": 12.2,
"lngprop": 1232323,
"strProp": "123",
"stringlist": [
"String1",
"String2",
"String3"
]
}
Option2 Convert/Serialize the object to XML. Use XStream for this,which will be the easiest of all available parsers. With just two lines of code,
XStream stream = new XStream();
System.out.println(stream.toXML(new YourHugeObject()));
Which will produce,
<com.kmg.jsontools.test.JSTest>
<stringlist>
<string>String1</string>
<string>String2</string>
<string>String3</string>
</stringlist>
<strProp>123</strProp>
<doubleProp>12.2</doubleProp>
<lngprop>1232323</lngprop>
<ct>
<a>1</a>
<b>sdf</b>
<f>12.0</f>
<nested>
<entry>
<string>key1</string>
<com.kmg.jsontools.test.Type1>
<kk>kk</kk>
<ssdf>123</ssdf>
</com.kmg.jsontools.test.Type1>
</entry>
<entry>
<string>onemorekey</string>
<com.kmg.jsontools.test.Type1>
<kk>kk</kk>
<ssdf>123</ssdf>
</com.kmg.jsontools.test.Type1>
</entry>
</nested>
</ct>
</com.kmg.jsontools.test.JSTest>
Either of the above approaches,you can either print the result to the console or to a file and inspect it manually. Alternatively you can also use reflection,in which case you would have to write a lot of code and significant amount of time in testing it as well.
How about using the ToStringBuilder from Jakarta-Commons org.apache.commons.lang.builder package
.
System.out.println ( ToStringBuilder.reflectionToString( YOUR_OBJECT ) );
Create some static method, assuming:
Map<String, Object> DebugHelper.breakDown(Object bean)
And implement it (for example) as datamining. Usage of org.apache.pivot.beans.BeanAdapter - for example can help you to treat the bean as plain Map, or (as you have written - you need mine in depth) use recursion to group all properties into single large Map
Another useful signature:
Map<String, Object> DebugHelper.breakDown(Object bean, String lookFor)
where second parameter used to find substring of value's from from Map of first method.
Why is this helpful? Because you can using eclipse's inspector to calculate the result of this method in any time of debugging
If the class is Serializable, I often use XStream to dump an object to xml.
If not, here's some code to maybe get you started on the reflection mode. It's not as recursive as you need but could be modified pretty easily.
public static void debugPrint(Object o1)
{
if(o1 == null)
{
System.out.println(o1);
return;
}
Class c = o1.getClass();
do
{
printFields(c, o1);
c = c.getSuperclass();
}
while(c != null);
}
private static void printFields(Class c, Object o1)
{
Field[] fields = c.getDeclaredFields();
for(Field field : fields)
{
printField(field, o1);
}
}
private static void printField(Field field, Object o1)
{
try
{
if(Modifier.isFinal(field.getModifiers())) return; //Skip this guy, he's trouble!
field.setAccessible(true);
Object val = field.get(o1);
System.out.println(field.getName() + ":" + toString(val));
}
catch(IllegalAccessException ex)
{
System.out.println("Could not access field" + field.getName());
ex.printStackTrace();
}
}
private static String toString(Object o)
{
if(o instanceof Object[])
return Arrays.toString((Object[])o);
else
return String.valueOf(o);
}
I think you need to use reflection to get the data. Here is some code that should get you started:
static String search(Object o, final String search) {
try {
inspect(new HashSet<Object>(), o, new StringWriter() {
public void write(String s) {
System.out.println(s);
if (s.indexOf(search) >= 0) {
throw new RuntimeException(s) {
public Throwable fillInStackTrace() { return null; } };
}
}
});
return "not found";
} catch (Exception e) {
return e.getMessage();
}
}
private static void inspect(HashSet<Object> ignore, Object o, Writer w)
throws Exception {
if (o == null) {
return;
}
for (Class<?> c = o.getClass(); c != null ; c = c.getSuperclass()) {
if (c.isArray()) {
int len = Array.getLength(o);
for (int i=0; i<len; i++) {
inspect(ignore, Array.get(o, i), w);
}
} else if (c.isPrimitive()) {
w.write(o.toString());
} else {
for (Field f : c.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) {
f.setAccessible(true);
if (f.getType().isPrimitive()) {
w.write(f.getName() + ": " + f.get(o));
} else {
if (!ignore.contains(o)) {
ignore.add(o);
w.write(f.getName());
inspect(ignore, f.get(o), w);
}
}
}
}
}
}
}
Some tips -
1 ) You can use shift and click on the variable / method / class, this will bring you to it correctly.
2 ) Right click - Open Call Hierarchy will show you exactly where the variable is called.
3 ) Command and O will show you an outline of teh class so you can navigate quickly
4 ) Quick Type Hierchy will show you how / where your variable sits in the grand scheme of the class.
Hope that helps
It often helps to print out the values of a large object so you can look over them carefully. In both Java and C++ I've written functions to print all the contents of an object, along with recursion to handle embedded objects. Debugging a hardcopy of the object's variables is old fashion, but for very complex situations it will lead to additional discoveries.
Maybe a totally different approach. Try to put your object (after decomposing) in in-memory data store (see http://www.google.de/search?hl=en&q=java+inmemory+data+store+search) and than use its search functionality. After performing tests you should get feeling if it performs better in your case. If you read the object intensively, maybe it will be even better to use in-memory database.
I think you have to use Java Reflection APIs. Take a look here: http://java.sun.com/developer/technicalArticles/ALT/Reflection/
Disclaimer: the code is brute-force and very ugly. I just want to demonstrate the possibility to do what OP wants with these basic ideas:
- Reflection
- Recursion
Field#setAccessible
- Special cases for array,
Iterable
,Enumeration
, primitive wrapper class andString
@Thomas Mueller's answer has elements of these but my #3 and #4 are slightly more robust. And his code doesn't seem to work with my crooked test classes. Yet I probably still missed some corner cases. Please don't send this to my Java tutor.
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
public class BreakDown
{
public static void main(String[] args)
{
printFields(new TestClass(), "");
}
private static void printFields(final Object obj, final String prefix)
{
if( basics.contains(obj.getClass()) )
{
System.out.println(prefix + obj);
return;
}
for( final Field f : obj.getClass().getDeclaredFields() )
{
try
{
try
{
printOneField(f, obj, prefix);
continue;
}
catch( SecurityException e ) {}
catch( IllegalAccessException e ) {}
AccessController.doPrivileged(new PrivilegedAction<Void>()
{
public Void run()
{
try
{
printOneField(f, obj, prefix);
}
catch( Exception e )
{
e.printStackTrace();
}
return null;
}
});
}
catch( Exception e )
{
e.printStackTrace();
}
}
}
private static void printOneField(Field f, Object obj, String prefix) throws Exception
{
f.setAccessible(true);
System.out.println(prefix + " |" + f.getName() + ":" + (obj = f.get(obj)));
if( obj == null )
return;
if( obj.getClass().isArray() )
{
for( int i = 0; i < Array.getLength(obj); ++i )
printObj(Array.get(obj, i), prefix, f.getName());
}
else if( obj instanceof Iterable )
{
Iterator<?> it = ((Iterable<?>)obj).iterator();
for( ; it.hasNext(); )
printObj(it.next(), prefix, f.getName());
}
else if( obj instanceof Enumeration<?> )
{
Enumeration<?> e = (Enumeration<?>)obj;
for( ; e.hasMoreElements(); )
printObj(e.nextElement(), prefix, f.getName());
}
else if( !basics.contains(obj.getClass()) )
printFields(obj, prefix + " |" + f.getName() + ":" + obj.getClass().getCanonicalName());
}
private static void printObj(Object o, String prefix, String name)
{
printFields(o, " " + prefix + " |" + name + ":[(" + o.getClass().getSimpleName() + ")");
}
private static final Set<Class<?>> basics = new HashSet<Class<?>>();
static
{
basics.add(Integer.class);
basics.add(Long.class);
basics.add(Double.class);
basics.add(Float.class);
basics.add(Boolean.class);
basics.add(Character.class);
basics.add(Byte.class);
basics.add(Void.class);
basics.add(Short.class);
basics.add(String.class);
}
}
class TestClass
{
public TestClass()
{
if( ++count_s < 3 )
list.add(new TestClass());
}
private static int count_s = 0;
public final String a = "a";
final TestClass2 obj = new TestClass2();
final TestClass2[] objArray = new TestClass2[]{new TestClass2(), new TestClass2()};
private final int b = count_s;
private final boolean[] c = new boolean[]{true, false};
private final List<TestClass> list = new ArrayList<TestClass>();
}
class TestClass2
{
private static int count_s = 0;
private final float d = 1.5f * (++count_s);
}
The output is:
|count_s:3
|a:a
|obj:TestClass2@6e1408
|obj:TestClass2 |count_s:9
|obj:TestClass2 |d:1.5
|objArray:[LTestClass2;@5483cd
|objArray:[(TestClass2) |count_s:9
|objArray:[(TestClass2) |d:3.0
|objArray:[(TestClass2) |count_s:9
|objArray:[(TestClass2) |d:4.5
|b:0
|c:[Z@19ee1ac
|c:[(Boolean)true
|c:[(Boolean)false
|list:[TestClass@1befab0]
|list:[(TestClass) |count_s:3
|list:[(TestClass) |a:a
|list:[(TestClass) |obj:TestClass2@13c5982
|list:[(TestClass) |obj:TestClass2 |count_s:9
|list:[(TestClass) |obj:TestClass2 |d:6.0
|list:[(TestClass) |objArray:[LTestClass2;@1186fab
|list:[(TestClass) |objArray:[(TestClass2) |count_s:9
|list:[(TestClass) |objArray:[(TestClass2) |d:7.5
|list:[(TestClass) |objArray:[(TestClass2) |count_s:9
|list:[(TestClass) |objArray:[(TestClass2) |d:9.0
|list:[(TestClass) |b:1
|list:[(TestClass) |c:[Z@14b7453
|list:[(TestClass) |c:[(Boolean)true
|list:[(TestClass) |c:[(Boolean)false
|list:[(TestClass) |list:[TestClass@c21495]
|list:[(TestClass) |list:[(TestClass) |count_s:3
|list:[(TestClass) |list:[(TestClass) |a:a
|list:[(TestClass) |list:[(TestClass) |obj:TestClass2@1d5550d
|list:[(TestClass) |list:[(TestClass) |obj:TestClass2 |count_s:9
|list:[(TestClass) |list:[(TestClass) |obj:TestClass2 |d:10.5
|list:[(TestClass) |list:[(TestClass) |objArray:[LTestClass2;@c2ea3f
|list:[(TestClass) |list:[(TestClass) |objArray:[(TestClass2) |count_s:9
|list:[(TestClass) |list:[(TestClass) |objArray:[(TestClass2) |d:12.0
|list:[(TestClass) |list:[(TestClass) |objArray:[(TestClass2) |count_s:9
|list:[(TestClass) |list:[(TestClass) |objArray:[(TestClass2) |d:13.5
|list:[(TestClass) |list:[(TestClass) |b:2
|list:[(TestClass) |list:[(TestClass) |c:[Z@15b7986
|list:[(TestClass) |list:[(TestClass) |c:[(Boolean)true
|list:[(TestClass) |list:[(TestClass) |c:[(Boolean)false
|list:[(TestClass) |list:[(TestClass) |list:[]
well if the complex object is composed by java objects like Strings, Lists, Maps, and such, you can by reflection iterate over them and when you find the "object" you're looking at is a String or int (and such) call toString
This wont work if your starting object contains a "custom" object
精彩评论