In the following Android sample, I have a set of JSON objects(referred to as templates) and I'm doing a sort of inheritance processing on them. If a JSON object has a key "extends", the corresponding value will refer to another JSON object. I just copy the mappings in the referred JSON object to the current one. Each property (referred to as component in the code) will have sub properties, so each sub property is copied individually. All the JSON objects are stored in a HashMap. EDIT: Posting sample code which demonstrates the issue.
package com.trial;
import java.util.HashMap;
im开发者_如何学运维port java.util.Iterator;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
public class Trial extends Activity {
HashMap<String, JSONObject> templates = new HashMap<String, JSONObject>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
templates.put("entity", new JSONObject("{\"Health\":{\"health\":50,\"maxHealth\":100}}"));
templates.put("unit", new JSONObject("{\"Health\":{\"health\":70},\"extends\":\"entity\"}"));
Iterator<String> templatesIterator = templates.keySet().iterator();
while (templatesIterator.hasNext()) {
String templateName = templatesIterator.next();
JSONObject template = templates.get(templateName);
if (template.has("extends"))
templates.put(templateName, processInheritance(template));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private JSONObject processInheritance(JSONObject template) throws JSONException {
if (template.has("extends")) {
//If current template extends from another template
String parentTemplateName = template.getString("extends");
template.remove("extends");
JSONObject parentTemplate = processInheritance(templates.get(parentTemplateName));
//Create a clone of the parent to which child properties/components can be added
JSONObject processedTemplate = new JSONObject(parentTemplate.toString());
Iterator<String> it = template.keys();
while (it.hasNext()) {
String componentName = it.next();
if (processedTemplate.has(componentName)) {
//Component is already present. Loop through Component properties and make
//specific replacements
JSONObject componentData = template.getJSONObject(componentName);
Iterator<String> it2 = componentData.keys();
JSONObject processedComponentData = processedTemplate.getJSONObject(componentName);
while (it2.hasNext()) {
String propertyName = it2.next();
processedComponentData.put(propertyName, componentData.get(propertyName));
}
processedTemplate.put(componentName, processedComponentData);
} else {
//Component is not already present. Simply copy
processedTemplate.put(componentName, template.get(componentName));
}
}
return processedTemplate;
} else {
return template;
}
}
}
On debugging I noticed that the code executes fine until after the while loop. Soon after the while loop, it jumps across the if/else and returns "template" instead of "processedTemplate". Even the resulting object indicates that "template" is being returned. I've tried clearing the binaries, recreating the emulator, and executing it on the actual device. The weird behavior persists. Could you someone tell me why this happens?
EDIT: The correct object IS being returned. My earlier comment that "template" was being returned was because of a bug where I was not saving the returned process object back into the HashMap. I was simply doing
if (template.has("extends"))
processInheritance(template));
However the control still seems to jump across. An problem with Eclipse, I suppose.
I tried to debug your code using Eclipse and it seems to work if I understand it's goal correctly. The debugger seems to behave as you described: when it should return processedTemplate
, it skips to the line with template
. However, processedTemplate
is returned, as expected. The debugger seems to jump out of a function always from the last return
statement.
I would say that the problem is not in this code. Maybe you should post some JSON example files with which the problem occurs.
My guess is because it's a recursive method and you are effectively removing a key and then iterating over the list of remaining keys, that this gets messed up somehow during recursion. Try not removing the "extends" key but rather ignore it when iterating through the key set.
The virtual machine would never have this kind of an error, so there is a good chance your recursive approach as a bug. I can't pinpoint it right away, but a couple System.out.println(template); might help.
Changing the code as below seems to have fixed the issue, but the problem with the original code snippet still remains.
private JSONObject processInheritance(JSONObject template) throws AssetLoadingException {
try {
if (!template.has("extends")) {
return template;
} else {
//Everything else
}
} catch (JSONException ex) {
throw new AssetLoadingException(ex);
}
}
This happens because you will processInheritance of all objects, until you get the root one. (The one that doesn't have an "extends" value.)
Hence, the first return that you find is the one in the else
branch.
Repeating my explanation:
Here is your striped-down code:
private JSONObject processInheritance(JSONObject template) throws AssetLoadingException {
try {
if (template.has("extends")) {
JSONObject parentTemplate = processInheritance(templates.get(parentTemplateName));
}
return processedTemplate;
} else {
return template;
}
} catch (JSONException ex) {
throw new AssetLoadingException(ex);
}
}
If you've got the sequence ROOT <- C <- B <- A
(where B extends A and so-on) and start evaluating A, this is what will happen:
- process A
- A is extending B (
has "extends")
, so process B - B is extending C, so process C
- C is extending ROOT, so process ROOT
- ROOT extends nothing so enter the
else
and return template.
(then there will be a sequence of returns after finishing processing C, B and A)
精彩评论