I have a calling method as genJsonPayload(). Inside this method I am calling a replace() method to replace some string with randomly generated values. I want to call the replace() by passing another method as parameter. My sample code below.
public static StringBuffer replace(String jsonparam, StringBuffer jsondata, String replacedWith) {
int start = 0;
int last = 0;
while (jsondata.indexOf(jsonparam)!= jsondata.lastIndexOf(jsonparam)) {
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start,last,replacedwith);
}
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start,last,replacedwith);
return jsondata;
}
public String genJSONPayload(String jsonpayload) {
StringBuffer sbuffer = new StringBuffer(jsonpayload);
try {
jsondata = replace("expectedAreaCode",jsondata, CreateRandom.createAreaCDRandom());
}
catch (Exception e) {
....
}
try {
jsondata = replace("expectedextensionNo",jsondata, CreateRandom.createExtensionNumberRandom());
}
catch (Exception e) {
....
}
}
Json payload as below :
{
...
...
"phonenumber": [
{
"areaCode": "expectedAreaCode",
"extensionNo": "expectedextensionNo",
...
}
{
"areaCode": "expectedAreaCode",
"extensionNo": "expectedextensionNo",
...
}
{
"areaCode": "expectedAreaCode",
"extensionNo": "expectedextensionNo",
...
}],
...
...
}
Note :
CreateRandom.createAreaCDRandom() return a random String.
Problem statement :
My requirement is to replace "expectedAreaCode" of json file with randomly generated area code using the replace() method. But as I am executing the CreateRandom.createAreaCDRandom() from genJSONPayload() only once so replacedWith in replace() method is getting store with fixed area code and for all the area code in the json array phonenumber, I am getting the same areacode.
Question :
Instead of passing the replacedWith String to the replace() method, can I pass the createAreaCDRandom() static method as parameter and instead of executing createAreaCDRandom() method from genJSONPayload, then I will execute it inside replace method multiple times inside the while loop. I have more than 5 such random mentods, basically for different json parameters.
How can I pass the createAreaCDRandom(), createExtensionNumberRandom(), etc methods as the parameter in the repace() method?
Use Supplier class as a method parameter (https://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html).
Then You can pass lambda or method reference to provide value generating logic.
public static StringBuffer replace(String jsonparam, StringBuffer jsondata, Supplier<String> supplier) {
int start = 0;
int last = 0;
while (jsondata.indexOf(jsonparam) != jsondata.lastIndexOf(jsonparam)) {
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start, last, supplier.get());
}
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start, last, supplier.get());
return jsondata;
}
public String genJSONPayload(String jsonpayload) {
StrignBuffer sbuffer = new StrignBuffer(jsonpayload);
try{
jsondata = replace("expectedAreaCode",jsondata, CreateRandom::createAreaCDRandom);
}
catch(Exception e){
....
}
try{
jsondata = replace("expectedextensionNo",jsondata, CreateRandom::createExtensionNumberRandom);
}
catch(Exception e){
....
}
}
Related
I need to write a code which would convert JSON file to CSV. The problem is in a format that the CSV file should look like.
Input json:
{
"strings":{
"1level1":{
"1level2":{
"1label1":"1value1",
"1label2":"1value2"
}
},
"2level1":{
"2level2":{
"2level3":{
"2label1":"2value1"
},
"2label2":"2value2"
}
}
}
}
And this is expected csv file for this json:
Keys,Default
1level1.1level2.1label1,1value1
1level1.1level2.1label2,1value2
2level1.2level2.2level3.2label1,2value1
2level1.2level2.2label2,2value2
I was trying to go through JSON file using recursion but this didn't work for me because of rewriting JSON object on each iteration and code was working only till the first value. Are there any suggestions about how can it be done?
Note: have tried to use different JSON libraries, so for now can be used any of them
UPDATE #1:
Non-working code example I was trying to use to go through JSON tree:
public static void jsonToCsv() throws JSONException {
InputStream is = MainClass.class.getResourceAsStream("/fromJson.json");
JSONTokener jsonTokener = new JSONTokener(is);
JSONObject jsonObject = new JSONObject(jsonTokener);
stepInto(jsonObject);
}
private static void stepInto(JSONObject jsonObject) {
JSONObject object = jsonObject;
try {
Set < String > keySet = object.keySet();
for (String key: keySet) {
object = object.getJSONObject(key);
stepInto(object);
}
} catch (JSONException e) {
Set < String > keySet = object.keySet();
for (String key: keySet) {
System.out.println(object.get(key));
}
e.printStackTrace();
}
}
UPDATE #2:
Another issue is that I will never know the names of the JSON object and count of child objects (update JSON and CSV examples as well to make the image more clear). All that is known, that it will always start with strings object.
Library used:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
So found a solution by myself:
public static void jsonToCsv() throws JSONException, IOException {
InputStream is = MainClass.class.getResourceAsStream("/fromJson.json");
JSONTokener jsonTokener = new JSONTokener(is);
JSONObject jsonObject = new JSONObject(jsonTokener).getJSONObject("strings");
builder = new StringBuilder();
while (!jsonObject.isEmpty()) {
stepInto(jsonObject);
}
String[] lines = builder.toString().split("\n"); // builder lines are in reverse order from expected so this array is used to reverse them
FileWriter csvWriter = new FileWriter("src/main/resources/toCsv.csv");
csvWriter.append("Keys,Default (en_US)\n");
for (int i = lines.length - 1; i >= 0; i--) {
csvWriter.append(lines[i]).append("\n");
}
csvWriter.flush();
csvWriter.close();
}
private static void stepInto(JSONObject jsonObject) {
for (String key: jsonObject.keySet()) {
Object object = jsonObject.get(key);
if (object instanceof JSONObject) {
builder.append(key).append(".");
stepInto(jsonObject.getJSONObject(key));
} else {
builder.append(key).append(",").append(object).append("\n");
jsonObject.remove(key);
break;
}
if (jsonObject.getJSONObject(key).isEmpty()) {
jsonObject.remove(key);
}
break;
}
}
I think you just missed keeping track of your result, otherwise it looks good.
Let's say your result is a simple string. Then you have to concatenate all keys while traversing the json object until you reach a primitive value (like a number or a string).
(I am writing this out of my head, so please forgive me for incorrect syntax)
private static String stepInto(JSONObject jsonObject) { // we change "void" to "String" so we can record the results of each recursive "stepInto" call
//JSONObject object = jsonObject; // we don't need that. Both variables are the same object
String result ="";
try {
for (String key: jsonObject.keySet()) { // shorter version
Object object = jsonObject.get(key); // Attention! we get a simple Java Object
if(object instanceof JSONObject){
result+= key+"."+stepInto(jsonObject.getJSONObject(key)); // the recursive call, returning all keys concatenated to "level1.level2.level3" until we reach a primitive value
}
if(object instanceof JSONArray){
result+= key+", "+ ... // notice how we use the csv separator (comma) here, because we reached a value. For you to decide how you want to represent arrays
}
result+= key +", "+ object +"\n"; // here I am not sure. It may well be that you need to check if object is a String an Integer, Float or anything.
}
} catch (JSONException e) {
for (String key: jsonObject.keySet()) {
System.out.println(object.get(key));
}
e.printStackTrace();
result+= "\n"; // I added this fallback so your program can terminate even when an error occurs.
}
return result; // sorry, I forgot to accumulate all results and return them. So now we have only one actual "return" statement that will terminate the call and return all results.
}
As you can see, I didn't change much of your original method. The only difference is that now we keep track of the keys ("level1.level2...") for each recursive call.
EDIT
I added a +"\n"; so everytime we reach a value so we can terminate that "line".
AND more importantly, instead of returning everytime, I add the result of each call to a string, so we continue looping over the keys and concatenate all results. Each call of the method will return only once all keys are looped over. (sorry that missed that)
In your calling method you could print out the result, something like that:
JSONObject jsonObject = new JSONObject(jsonTokener);
String result = stepInto(jsonObject);
System.out.println(result);
I am writing my own JSON parser in Java and I am looking for a way to split a string by a comma or a colon, that are outside [], {} or "" pairs.
I found a tutorial via Google, and it works. The issue is, that it also captures the commas inside those brackets, and I need it to ommit them. I don't know how to edit the regular expression to exclude captured commass from one of these bracket pairs. I tried something like ",(?=([^\"\\{\\[]*\"[^\"\\}\\]]*\")*[^\"]*$)", but it doesn't work. It messes it up even more. The same also applies to the colon separation, which is used in separation of key and value of a JSON object.
Is there a way to combine the "", {} and [] pairs together in the regex in such way that it works? Sorry if I look like a lame, but I really can't figure out how the regex should look like.
BTW, this is a code snippet I want to use it in:
public class JavaJSON {
private HashMap<String, Object> content;
// Constructors
/**
* Create new empty JSON object
*/
public JavaJSON() {
this.content = new HashMap<>();
}
// ...
/**
* Parse a JSON string to a JSON object
*
* #param JSON JSON string to be converted to JSON object
* #return JSON object from given string
*/
public static JavaJSON parse(#NotNull String JSON) {
if (!JSON.startsWith("{") || !JSON.endsWith("}")) return null;
// If this is not a valid JSON string, return nothing.
JavaJSON output = new JavaJSON();
String content = JSON.substring(1, JSON.length() - 1);
if (content.length() == 0) return output; // if empty, return an empty JSON object
// Regex literals
String commaSeparated = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"; // TODO: Change commaSeparated to capture any {} [] "" pair group
String colonSeparated = ":(?=([^\"]*\"[^\"]*\")*[^\"]*$)"; // TODO: Change colonSeparated to capture any {} [] "" pair group
String[] tokens = content.split(commaSeparated);
if (tokens.length == 0) return null;
// Don't know exactly if this is going to happen, but better be sure
for (String token : tokens) {
String rawToken = token.trim();
if (rawToken.length() == 0) return null;
// Omitted comma, extra comma, etc. = JSON error
String[] mapToken = rawToken.split(colonSeparated);
if (mapToken.length < 2 || mapToken.length > 2) return null;
// Expected format = {"foo": "bar"}; format isn't valid
String mapKey = mapToken[0].trim();
String mapValue = mapToken[1].trim();
if (!mapKey.startsWith("\"") || !mapKey.endsWith("\"")) return null;
// Key must be a string
String rawMapKey = mapKey.substring(1, mapKey.length() - 1); // get quote-less variant
if (rawMapKey.length() == 0) return null;
// Key must not be empty
// check errors
if (mapValue.startsWith("{") && !mapValue.endsWith("}")) return null;
// Not a valid JSON object
if (mapValue.startsWith("[") && !mapValue.endsWith("]")) return null;
// Not a valid JSON array
if (mapValue.startsWith("\"") && !mapValue.endsWith("\"")) return null;
// Not a valid string
// get value object
Object rawMapValue;
// parse value object
if (mapValue.startsWith("\"") && mapValue.endsWith("\"")) {
rawMapValue = mapValue.substring(1, mapValue.length() - 1);
} else if (mapValue.startsWith("{") && mapValue.endsWith("}")) {
rawMapValue = parse(mapValue);
} else if (mapValue.startsWith("[") && mapValue.endsWith("]")) {
rawMapValue = parseArray(mapValue);
} else {
try {
rawMapValue = Long.parseLong(mapValue);
} catch (Exception e) {
try {
rawMapValue = Double.parseDouble(mapValue);
} catch (Exception f) {
return null;
// Not a valid number
}
}
}
output.update(rawMapKey, rawMapValue);
}
return output;
}
// ...
}
Here is an example of an issue I am having using reflection. This is a simple case, but what I eventually need is to dynamically build the method name on the fly... but even this simple case I can not get to work!
Client1 cData = (Client1) session.get(Client1.class, 1);
int cType = cData.getClientType();
int cType2 = -1;
Method method[] = null;
Method getCTypeMethod = null;
try {
method = cData.getClass().getMethods();
for (Method m : method){
System.out.println(m.getName()); // displays getClientType
}
getCTypeMethod = cData.getClass().getMethod("getClientType", int.class);
if (getCTypeMethod != null){
cType2 = (int) getCTypeMethod.invoke(cData, int.class);
}
} catch (Exception e) {
e.printStackTrace();
}
assertEquals(cType, cType2);
The line:
getCTypeMethod = cData.getClass().getMethod("getClientType", int.class);
Always throws an exception:
java.lang.NoSuchMethodException: Client1.getClientType(int)
The method getMethod receives the param classes, not the return type, your getClientType receive a int?
If not, try:
cData.getClass().getMethod("getClientType");
Hi guys I'm developing a Restful + JSON servlet, but I got a strange problem, if i try to compile my project source with this method:
public
static
WipdDBTable
parseJSON(String JSONBody)
{
JSONObject jsonObj;
JSONTokener jsonTok;
Iterator it;
String[] labels;
String[][] fields;
int i;
try {
jsonTok = new JSONTokener(JSONBody);
jsonObj = new JSONObject(jsonTok);
labels = new String[jsonObj.length()];
fields = new String[1][labels.length];
i = 0;
it = jsonObj.keys();
while(it.hasNext()) {
String key = it.next().toString();
labels[i] = key;
fields[0][i] = jsonObj.get(key);
i++;
}
return new WipdDBTable(labels, fields);
} catch(JSONException ex) {
return null;
}
}
I get this error:
WipdJSON.java:102: incompatible types
found : java.lang.Object
required: java.lang.String
fields[0][i] = jsonObj.get(key);
So I wrote a test class, with apparently the same source, but with this one I don't get any error:
public class jsontest
{
public static
void
main(String[] args)
{
String s1;
JSONObject jsonObj;
JSONTokener jsonTok;
s1 = "{\"lo\":\"ol\",\"json\":{\"1\":\"2\"},\"yo\":{\"lol\":\"lol\"}}";
try {
jsonTok = new JSONTokener(s1);
jsonObj = new JSONObject(jsonTok);
Iterator it = jsonObj.keys();
while(it.hasNext()) {
String key = it.next().toString();
System.out.print(key + "=>");
System.out.println(jsonObj.get(key));
}
} catch(JSONException ex) {
ex.printStackTrace();
}
}
}
jsonObj.get(key); return an Object, you need to cast it to a String if you want to get the value in a String variable. There is a getString method in the JSONObject class:
fields[0][i] = jsonObj.getString(key);
And you don't get any error in your test class as you're only outputting the value that is calling the toString() method.
fields[0][i] = jsonObj.get(key);
Change it to
fields[0][i] = (String) jsonObj.get(key);
Now, you are casting Object to String. if you are not sure it is String always check with instanceof operator.
Object obj = jsonObj.get(key);
if(obj instanceof String)
{
fields[0][i] = (String) jsonObj.get(key);
}
get method signature is look likes this
java.lang.Object get(java.lang.String key)
so definitely you have to cust it in your actual type.
for example
String key = (String)jsonObj.get(key);
Hi the error can be romove by replaceing line
fields[0][i] = jsonObj.get(key);
with
fields[0][i] = (String)jsonObj.get(key);
and it is not giving error in second code snippet because you are printing it in System.out.println but not assigning it to any String variable
System.out.println(jsonObj.get(key));
so if you try to assign it in any String variable without casting it in String variable it will also give error.
I am new to eclipse plugin development and I am trying to convert a IMethod to a string representation of the full method name. I.E.
my.full.package.ClassName.methodName(int param, String string)
so far I have had to hand roll my own solution. Is there a better way?
private static String getMethodFullName(IMethod iMethod)
{
String packageString = "[Default Package]";
try {
IPackageDeclaration[] declarations = iMethod.getCompilationUnit().getPackageDeclarations();
if(declarations.length > 0)
{
packageString = declarations[0].getElementName();
}
} catch (JavaModelException e) {
}
String classString = iMethod.getCompilationUnit().getElementName();
classString = classString.replaceAll(".java", "");
String methodString = iMethod.getElementName() + "(";
for (String type : iMethod.getParameterTypes()) {
methodString += type + ",";
}
methodString += ")";
return packageString + "." + classString + "." + methodString;
}
You can get the Fully qualified name for the type using
method.getDeclaringType().getFullyQualifiedName();
This is probably easier than accessing the package from the compilation unit. The rest of you function looks correct.
One small point: you should use StringBuilder to build up the string instead of adding to a standard String. Strings are immutable so addition creates loads of unrecesary temparary objects.
private static String getMethodFullName(IMethod iMethod)
{
StringBuilder name = new StringBuilder();
name.append(iMethod.getDeclaringType().getFullyQualifiedName());
name.append(".");
name.append(iMethod.getElementName());
name.append("(");
String comma = "";
for (String type : iMethod.getParameterTypes()) {
name.append(comma);
comma = ", ";
name.append(type);
}
name.append(")");
return name.toString();
}
Thanks to iain and some more research I have come up with this solution. It seems like something like this should be built into the JDT....
import org.eclipse.jdt.core.Signature;
private static String getMethodFullName(IMethod iMethod)
{
StringBuilder name = new StringBuilder();
name.append(iMethod.getDeclaringType().getFullyQualifiedName());
name.append(".");
name.append(iMethod.getElementName());
name.append("(");
String comma = "";
String[] parameterTypes = iMethod.getParameterTypes();
try {
String[] parameterNames = iMethod.getParameterNames();
for (int i=0; i<iMethod.getParameterTypes().length; ++i) {
name.append(comma);
name.append(Signature.toString(parameterTypes[i]));
name.append(" ");
name.append(parameterNames[i]);
comma = ", ";
}
} catch (JavaModelException e) {
}
name.append(")");
return name.toString();
}
I am not sure it would take into account all cases (method within an internal class, an anonymous class, with generic parameters...)
When it comes to methods signatures, the classes to look into are:
org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedMethodsOperation
org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2
You need to get the jdt.core.dom.IMethodBinding, from which you can extract all what you need.
If you have a MethodInvocation, you can:
//MethodInvocation node
ITypeBinding type = node.getExpression().resolveTypeBinding();
IMethodBinding method=node.resolveMethodBinding();