You can use some API, like Jackson, for creating JSON object and print it into string. First create a json ArrayNode for your items. Then for each string in your items, create an ObjectNode like this,
ObjectNode node = mapper.createObjectNode();
node.put("result", "xyz");
and add them to the ArrayNode. Finally you print the JSON object out.
Well, you can't. As you said, you can represent arrays and dictionaries. You have two choices.
Represent the set as an array. Advantage: Converting from set to array and back is usually easy. Disadvantage: An array has an implied order, which a set doesn't, so converting identical sets to JSON arrays can create arrays that would be considered different. There is no way to enforce that array elements are unique, so a JSON array might not contain a valid set (obviously you could just ignore the duplicates; that's what is likely to happen anyway).
Represent the set as a dictionary, with an arbitrary value per key, for example 0 or null. If you just ignore the values, this is a perfect match. On the other hand, you may have no library support for extracting the keys of a dictionary as a set, or for turning a set into a dictionary.
In my programming environment, conversion between set and array is easier (array to set will lose duplicate values, which either shouldn't be there, or would be considered correct), so for that reason I would go with arrays. But that is very much a matter of opinion.
BUT: There is a big fat elephant in the room that hasn't been mentioned. The keys in a JSON dictionary can only be strings. If your set isn't a set of strings, then you only have the choice of using an array.
Don't try to represent sets in JSON. Do it when parsing the data instead.
Your JSON data should have a schema which specifies which fields should be treated as a set, or you may have a metadata embedded in the JSON data itself that describes when a list should be treated as a set (e.g. {"houses": {"_type": "set", "value": [...]}}) or with a naming convention.
Note that according to JSON standard, a JSON object can have duplicate keys. ECMA-404 wordings:
Objects
[...] The JSON syntax does not impose any restrictions on the strings used as names, does not require that name strings be unique, and does not assign any significance to the ordering of name/value pairs. These are all semantic considerations that may be defined by JSON processors or in specifications defining specific uses of JSON for data interchange.
AFAICD, nothing in the spec forbids non unique names, and there are many JSON parser implementations that can parse non unique object names. RFC 7159 discourages non unique names for interoperability, but specifically don't forbid it either, and goes on to list how various parsers have been seen handling non unique object names.
And ECMA 404 also doesn't require that array ordering be preserved:
Arrays
The JSON syntax does not define any specific meaning to the ordering of the values. However, the JSON array structure is often used in situations where there is some semantics to the ordering.
This wording allows applications to use arrays to represent sets if they so choose.
java - Can't parse HashSet to JSONObject String - Stack Overflow
Convert Set to JSONArray in Java 8 - Code Review Stack Exchange
Converting JSON Data to a set of Java objects - Stack Overflow
Can't get jdt language server to work on coc-java extensions for coc-nvim extension
Just idly wondering if it just works with YCM
More on reddit.comVideos
I think this is a problem with org.json.simple library.
I have used org.json library, and have to do some minor changes in above code to work:
JSONObject json = new JSONObject();
json.put("set", new HashSet<>(Arrays.asList("a", "b")));
json.put("list", Arrays.asList("a", "b"));
String jsonString = json.toString();
System.out.println(jsonString);
JSONObject afterParse = new JSONObject(jsonString);
System.out.println(afterParse.toString());
The output of this code is:
{"set":["a","b"],"list":["a","b"]}
when u convert a array of strings to list and then the list to a Set, it is no longer String, but an array of objects hence new HashSet<>(Arrays.asList("a", "b"))); gives "set":[b, a] (without quotes). And parser.parse(jsonString); works on Object not array of Objects.
Try using a list instead of a set as below :
json.put("set", new Arraylist<>(new HashSet<>(Arrays.asList("a", "b"))));
- You should not catch exceptions and discard them silently. This code has no chance of throwing an exception, therefore you should just let the exception bubble up and let someone else catch it. If it should ever happen.
- Your variable names don't express their intention. The word
tempshould not be used at all, you mix terminology betweenlanguagesandlocale. Sometimes you put thejat the beginning of the name, sometimes at the end; this is inconsistent. - Declare your variables as late as possible (
json). - Declare your variables in the smallest possible scope (
tempj). - Method names in Java start with a lowercase letter.
- Don't declare
throws Exceptionwhen your code doesn't actually do that. - It is unusual that the
languagesset actually containsnull. So in general you should leave out theif (language != null)check.
After following all these hints, the code may look like this:
public String createLanguagesJson(Set<Locale> languages) {
JSONArray array = new JSONArray();
for (Locale language : languages) {
array.put(new JSONObject()
.put("lcode", language.toLanguageTag())
.put("ldisplay", language.getDisplayName()));
}
return new JSONObject()
.put("root", array)
.toString();
}
I really suggest to keep this implementation nearly as it is. It is clear and direct.
Maybe you decompose the method into two methods.
Maybe you reduce scope of one variable (tempj).
Maybe you rename some artefacts.
public String toJSONString(Set<Locale> languageSet) throws Exception {
JSONObject root = new JSONObject();
try {
JSONArray localeAsJSONObjectArray = new JSONArray();
for (Locale locale : languageSet) {
if (locale != null) {
localeAsJSONObjectArray.put(toJSONObject(locale));
}
}
root.put("root", localeAsJSONObjectArray);
} catch (JSONException e) {
//
}
return root.toString();
}
private JSONObject toJSONObject(Locale locale) {
JSONObject localeAsJSONObject = new JSONObject();
localeAsJSONObject.put("lcode", locale.toLanguageTag());
localeAsJSONObject.put("ldisplay", locale.getDisplayName());
return localeAsJSONObject;
}
Anything else will distort the intention. Introducing lambdas here will make me think that you want to make the algorithm fit to lambda as the direction should be vice versa: The used control structures should follow your algorithm.
You have to keep one thing in mind:
Lambdas and streams really make sense if you use ".parallelStream()" instead of ".stream()". Then your code will act in some parallel way with the Fork-Join-Thread-Pool and your code speeds up. Anything else is Old wine in new bottles. Occasionally lambdas seem to be more expressive. In other situations you will break your leg if you want to fit you algorithm to lambda and you loose expressiveness.
But to use ".parallelStream()" your code has to meet some not obvious requirements that is clear to those who already work with parallelism. Most of the time you are not able to simply change ".stream()" to ".prallelStream()". So most of the time you will not benefit from the new API.
If you still want to use lambdas I provide you an example that is surely not strcutral optimizied but that shows the elements to consider if you really want to benefit from parallelism.
public static String toJSONString(Set<Locale> languageSet) {
JSONObject root = new JSONObject();
JSONArray localeAsJSONObjectArray = new JSONArray();
// synchronization, as putting may occur asynchronously. JSONArray itself is NOT synchronized.
Consumer<JSONObject> synchronizedJSONObjectAdder = jsonObject -> {
synchronized (localeAsJSONObjectArray) {
localeAsJSONObjectArray.put(jsonObject);
}
};
// function that defines the transformation from a locale to a JSONObject.
// no synchronization needed as every result only depends on the function parameter.
Function<Locale, JSONObject> localeToJSONObjectFunction = locale -> {
JSONObject localeAsJSONObject = new JSONObject();
localeAsJSONObject.put("lcode", locale.toLanguageTag());
localeAsJSONObject.put("ldisplay", locale.getDisplayName());
// for debugging in which thread the transformation will be processed.
System.out.println(Thread.currentThread().getName());
return localeAsJSONObject;
};
// using parallel stream to address parallelism
languageSet.parallelStream().filter(locale -> locale != null).map(localeToJSONObjectFunction).forEach(synchronizedJSONObjectAdder);
root.put("root", localeAsJSONObjectArray);
return root.toString();
}
public static void main(String[] args) throws Exception {
toJSONString(new HashSet<>(Arrays.asList(Locale.getAvailableLocales())));
}
With Jackson, it's as simple as using an ObjectMapper and creating a POJO to represent your JSON object:
public class Person {
private int age;
private String name;
private List<String> messages;
// getters/setters
}
And then:
String json = "{\"age\":100,\"name\":\"mkyong.com\",\"messages\":[\"msg 1\",\"msg 2\",\"msg 3\"]}";
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json.getBytes(), Person.class);
System.out.println(person.getMessages()); // [msg 1, msg 2, msg 3]
To do the reverse, use writeValue:
Person person = new Person();
person.setAge(100);
// ...
mapper.writeValue(..., person);
Jackson is a popular framework for converting between JSON and Java POJOs.