How do I loop over a string of item and create an object based on that?
I currently have this code:
public static Object ParseParams(String string)
{
Object params = new Object();
String[] lines = string.split("\n");
for(String line : lines)
{
String[] splittedLine = line.split("=");
params[splittedLine[0]] = splittedLine[1]; //JavaScript syntax, not Java!
}
return params;
}
The input string is in this format:
param1=value1
param2=value2
foo=bar
How do I fix the problematic line?
Edit
Sometimes the string would look like this:
foo=bar
param=1=hello
param=2=world
Would it be possible with Maps in Java to get the output like this:
foo
bar
param
1
hello
2
world
So the Maps are sometimes nested, and it you would retrieve hello by calling params.get("param").get("1");
It sounds like you want either a Map, or a JSON library.
Maps
public static Map<String, String> ParseParams(String string)
{
Map<String, String> params = new HashMap<String, String>();
String[] lines = string.split("\n");
for(String line : lines)
{
String[] splittedLine = line.split("=");
params.put(splittedLine[0], splittedLine[1]);
}
return params; // Get a param with params.get(key);
}
In general you'll want a HashMap (fast but not stored in order), but there's also a TreeMap which is slightly slower but stored in order (which can be useful sometimes).
JSON
The format JavaScript uses for objects is used as a general-purpose storage format called JSON (JavaScript Object Notation). This is only useful for storage / printing / network transmission. Internally, Java JSON libraries use maps (JavaScript interpreters probably do too).
There are several Java APIs, but StackOverflow users seem to recommend Json-lib:
public static JSONObject ParseParams(String string)
{
// Note that we need everything from the other method anyway
return JSONObject.fromObject(ParseParams(string));
}
EDIT:
You're already reaching the point where using a Map is strained. I'd suggest just using a class:
class MyStuff {
String foo;
Map<String, String> params;
}
It is possible to nest Maps, like Map<String, Map<String, String>> or Map<String, Object>, but you really should be using classes for this.
Related
Is there a way to get or set an array element stored in a Java Map?
Example:
If we have a map like this:
{
name: "Blah",
friends: ["Foo", "Bar"]
}
Map<String, Object> myMap = new HashMap<>();
List<String> friends = new ArrayList<>();
myMap.put("name", "Blah");
myMap.put("friends", friends);
Is it possible to use Reflection to get or set the first element in the friends array in the "myMap" from the string: "myMap.friends[0]"
Your question is not very clearly written and I believe that's why you are not getting the answer you expect but, If I understood your question correctly, you need to parse the following input string at runtime that you don't know beforehand:
myMap.friends[0]
And this should be parsed into components like:
mapName = "myMap"
mapKey = "friends"
valueIndex = 0
And with this information, you need to manipulate data in a Map at runtime through reflection.
Note: This only makes sense if you could potentially have more complex expressions, using different sort of objects and accessing nested properties of retrieved objects, otherwise you wouldn't need reflection at all.
Note 2: You may want to have a look at JXPath which already does a lot of this for you based on a XPath-like syntax for navigating object graphs.
That said, if my assumptions are correct and you still want to do it yourself, consider the following example.
For the sake of demonstration, let's consider our map is returned by a method myMap inside a Context.
private static class Context {
public Map<String, Object> myMap() {
Map<String, Object> myMap = new HashMap<>();
List<String> friends = new ArrayList<>();
friends.add("Foo");
friends.add("Bar");
myMap.put("name", "Blah");
myMap.put("friends", friends);
return myMap;
}
}
I'm assuming you are already parsing the input string into the different components. If not, for this simple string you could do it with simple regular expressions. If you already have the components, let's consider the following method:
public static Object readContextMap(Context context,
String mapName, String mapKey, Integer mapValueIndex) throws Exception {
// gets Context class for inspection
Class<?> cls = context.getClass();
// search for a method based on supplied mapName
Method mapMethod = cls.getDeclaredMethod(mapName);
// get a value from the retrieved map based on mapKey
Object mapValue = mapMethod.getReturnType()
.getDeclaredMethod("get", Object.class)
.invoke(mapMethod.invoke(context), mapKey);
// if the result is of type list, use the index to return the indexed element
if (List.class.isAssignableFrom(mapValue.getClass())) {
return ((List<?>)mapValue).get(mapValueIndex);
}
// otherwise return the object itself
return mapValue;
}
For testing purposes, consider the following main method:
public static void main(String[] args) throws Exception {
Context context = new Context();
String input = "myMap.friends[0]";
// parse input into...
String mapName = "myMap";
String mapKey = "friends";
Integer valueIndex = 0;
Object firstFriend = readContextMap(context, mapName, mapKey, valueIndex);
System.out.println(firstFriend);
// prints Foo
Object name = readContextMap(context, "myMap", "name", null);
System.out.println(name);
// prints Blah
}
This should be approximately what you want. You can easily create variations of this to set values as well. Please bear in mind that this code is just for demo purposes and needs a better error handling (e.g. verify if the context is really returning a map and nothing else).
This should be something along the lines you are looking for.
There's no need to use reflection here. You can simply cast it (which is also unsafe, but less so).
You can just do this:
List<String> friends = (List<String>) myMap.get("friends");
friends.set(0, "Bob");
I have a class with two methods: the startAPI() calls the API classes to extract entities and returns the entities and the occurrence of the entities. I need this return value in two different methods from another class, but as soon as I call the second method (countApiOcc()) the map I pass is empty. How can I use the returned map in two different methods?
public class Topic {
public void calculateNoFeedback(String language, List<String> api, List<String> corr) {
Map<String, Object> apis = startAPI(api, textList);
CountTopics countT = new CountTopics();
ArrayList<String> topics = countT.getTopics(apis);
countT.countApiOcc(topics, apis);
}
public Map<String, Object> startAPI(List<String> selectedAPI, List<String> text) {
Map<String, Object> apisValues = new HashMap<String, Object>();
//do stuff to extract the entities and return entities
return apisValues;
}
}
The CountTopic() class looks as follows, and, explained in short, user can select which or how many APIs he wants to use to extract entities and in the class CountTopic() the method getTopics() should find the topics every selected API found and countApiOcc() I need the frequency of the selected entities (all of this works) it is just the map I need in the second method.
public ArrayList<String> getTopics(Map<String, Object> apiV) {
System.out.println("apiV: "+apiV);
Iterator iterator = apiV.entrySet().iterator();
mapSize = apiV.size();
System.out.println("Size of the map: "+ mapSize);
while (iterator.hasNext()) {
Map.Entry entries = (Map.Entry)iterator.next();
String key = entries.getKey().toString();
switch(key) {
case "valuesMS":
Map<String, Object> mapMicrosoft = (Map<String, Object>) apiV.get(key);
ArrayList<String> microsoft = (ArrayList<String>) mapMicrosoft.get("topicArrayMS");
microsoftTopicLowerCase.addAll(microsoft);
topicsMultiset.addAll(microsoft);
break;
case "valuesGate":
Map<String, Object> mapGate = (Map<String, Object>) apiV.get(key);
ArrayList<String> gate = (ArrayList<String>) mapGate.get("topicArrayGA");
//store the values for finding the topics which are found from every selected API
//store the values from the api to lower case to find the index later (needed for how often this api found the topic
gateTopicLowerCase.addAll(gate);
topicsMultiset.addAll(gate);
break;
}
iterator.remove();
}
//rest code: compare the Arrays to find the same topics
iterator.remove();
There's your culprit. You're emptying your map. Don't do this, and it will work. From my limited view on your code, there doesn't seem to be any reason to modify your map. But in case it would be necessary, you should make a copy of the map at the beginning of your method, and work on this copy. Generally it's a bad idea to modify your input parameters, unless that is the specific purpose of that method.
I have come across this question on StackOverflow which asks about converting JSON to Java. The answer shows that another class is modelled to represent the JSON data as well as an object being created and I don't understand why.
Does that object now contain all the information after Gson reads the content or only one key/value pair? If it only contains 1 key/value pair, I'm assuming I would need to create multiple objects for the JSON that I have below which I can the use a loop to iterate over and add the values to a drop down menu?
{
"1": "Annie",
"2": "Olaf",
"3": "Galio",
"4": "TwistedFate",
"5": "XinZhao",
"6": "Urgot",
"7": "Leblanc",
"8": "Vladimir",
"9": "FiddleSticks",
"10": "Kayle",
"11": "MasterYi",
"12": "Alistar",
"13": "Ryze",
"14": "Sion",
"15": "Sivir",
"16": "Soraka",
"17": "Teemo",
"18": "Tristana",
"19": "Warwick",
"20": "Nunu"
}
Essentially what I am aiming to do is:
1) Create a list of names with the Values.
2) Sort the list of names (as it comes unsorted) in alphabetical order
3) Loop through the list and add each name to a drop down menu
4) When a name in the drop down menu is selected, the key associated with that value is passed to another url which receives more data.
Sorry if this is unclear. I've spent a couple of hours trying to understand how to get elements from JSON and display it, as well as trying to create a list where I can use the key to display information the name but have had no luck except for using a for-each loop.
Let's use Jackson's feature that allows you to map any property to a single method (you don't really need a getter here I believe). Just swap the key and value in this universal setter, and add to a TreeMap, which is already sorted by key (name). Then you can output the keys (names) in the alphabetical order and get an ID by name easily.
public static void main(String[] args) throws IOException {
String json = "....."; // your JSON string here
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
ReverseMap pairs = mapper.readValue(json, ReverseMap.class);
for (Map.Entry<Object, String> entry : pairs.getValues().entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
public class ReverseMap {
private TreeMap<Object, String> mapping = new TreeMap<>();
#com.fasterxml.jackson.annotation.JsonAnySetter
public void add(String name, Object value) {
mapping.put(value, name);
}
public Map<Object, String> getValues() {
return mapping;
}
}
Gson Bean Mapping Solution
Okay, what you have is a bit unusual for a JSON object; the keys (the numbers in your case) essentially represent properties of their contained object. That's workable, but you have to understand that, for example, when looking for "Annie" in the JSON object, if you use Gson to map to a "bean" class, which we'll call Data (as in the linked example), then you'd have to create a data object like so:
class Data {
private String _1;
// ...
private String _20;
public String get1() { return _1; }
public void set1(String _1) { this._1 = _1; }
// ...
public String get20() { return _20; }
public void set20(String _20) { this._20 = _20; }
}
And by using Data data = new Gson().fromJson(myJsonString, Data.class); on the given string, you'd be able to find "Annie" by calling... uh... data.get1()?
Clearly, this isn't a good solution.
Better Solutions
Since your data doesn't follow the typical format for a JSON object, you have two options:
If you can, refactor your JSON representation to a more verbose, but better representation for parsing.
Use a different approach to parse the existing JSON.
Solution 1: Changing the JSON representation
Refactoring the JSON would result in an object that (preferably) would look like this:
{
"champions" : [
{
"index" : 1,
"name" : "Annie"
},
{
"index" : 2,
"name" : "Olaf"
},
// ...
]
}
This could map easily to a couple of beans that look like this:
class Data {
private List<Champion> champions;
// TODO getters and setters
}
class Champion {
private int index;
private String name;
// TODO getters and setters
}
However, this adds a lot of unnecessary clutter to the JSON object, and isn't really necessary with only two fields per champion (the name, and their index).
You could simplify that further like so:
{
"champions" : [
"Annie",
"Olaf",
// ...
]
}
The bean class for that would then be:
class Data {
private List<String> champions;
// TODO getters and setters
}
Much simpler, but still requires a change to the JSON you're getting, which in some situations isn't possible. If you used this, though, you could also get rid of the "bean" class entirely, via:
List<String> champions = (List<String>) new Gson().fromJson(myJsonString, new TypeToken<List<String>>(){}.getType());
Solution 2: Changing how the JSON is parsed
The arguably better and cleaner solution is just to change how the JSON is parsed.
The goal here (if I understand you correctly) is to parse the JSON and spit out a collection of strings representing each champion's name, accessible by the numeric index of the champion in the JSON representation.
As such, and because of the way the JSON object is laid out as a simple mapping of strings to strings, we can use Gson to pipe directly into a Map<String, Object>, like so:
Map<String, String> mappedValues = new Gson().fromJson(myJsonString, Map.class);
String anniesName = mappedValues.get("1"); // "Annie"
String olafsName = mappedValues.get("2"); // "Olaf"
boolean hasTwentyOneElements = mappedValues.containsKey("21"); // false
This is shorter, requires no "bean" classes, and keeps the original JSON representation. The downside is that you can't easily tell whether the indices of each entry are correct and consistent; ie. if someone types in the wrong number, or deletes one of the entries.
To get a container of all keys, you just use mappedValues.keySet(), and to get a container of all key-value pairs, you use mappedValues.entrySet(), which gives you a Set<Map.Entry<String, String>>. Both of those can be iterated over, and may be in random order (I'm not sure whether the underlying Map implementation preserves insertion order or not).
To get the index for a given name (ie. champ), you'd use something similar to the following:
String index = null;
for (Map.Entry<String, String> entry : mappedValues.entrySet()) {
if (champ.equals(entry.getValue())) {
index = entry.getKey();
break;
}
}
Of course, you'd have to check to see if index is null after this, and handle that appropriately, but it's easily doable.
EDIT: #vempo's answer provides a cleaner, more efficient lookup strategy by means of inverting the map (although the answer is written for Jackson, instead of Gson); an adaptation of this for Gson is as follows (and yes, there is a vastly superior version in java-8, left out for sake of availability):
public Map<String, String> invertMap(Map<String, String> input) {
Map<String, String> newMap = new LinkedTreeMap<String, String>(); // TODO Pick optimal storage class
for (Map.Entry<String, String> entry : input.entrySet()) {
newMap.put(entry.getValue(), entry.getKey());
}
return newMap;
}
// ...
Map<String, String> mappedValues = invertMap(new Gson().fromJson(myJsonString, Map.class));
String annieIndex = mappedValues.get("Annie"); // "1"
String olafIndex = mappedValues.get("Olaf"); // "2"
It's worth noting that this sacrifices efficiency of constructing the map by effectively building it twice (once by Gson and once more to invert), but it makes value lookup much more efficient.
First, my apologies as I am a brand new Java developer and have been tasked to work on a massive application, so I am very out of my league.
I currently have this code. Basically, it looks for some values on the URL of a web application, and then passes the name of the URL variable and the value of the URL variable into the application, where it will then be added to an output stream of text. So the variables would look like "&LeadID=123" which would translate to the "LeadId" and "123". At that point, we reformat it into an XML value, such as
<id sequence="1" source="leadId">123</id>
This is the code that is responsible for checking for the variables, and then putting them into a Map, and then (in theory) passing them to the code which will format them:
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Map<String, String> ProspectTrackingKeys = new HashMap<String, String>();
if (recipSettings.containsKey("leadId")) {
ProspectTrackingKeys.put("LeadId", recipSettings.get("leadId"));
}
if (recipSettings.containsKey("wtk")) {
ProspectTrackingKeys.put("wtk", recipSettings.get("wtk"));
}
if (recipSettings.containsKey("efd")) {
ProspectTrackingKeys.put("efd", recipSettings.get("efd"));
}
if (recipSettings.containsKey("aid")) {
ProspectTrackingKeys.put("aid", recipSettings.get("aid"));
}
if (recipSettings.containsKey("oth")) {
ProspectTrackingKeys.put("oth", recipSettings.get("oth"));
}
list.add(ProspectTrackingKeys);
prospect.setProspect(adaptor.convertProspectIds((Map<String, String>) prospectIds));
However, the last line of code (above) is generating these errors:
The method setProspect(ArrayList) is undefined for the type Prospect
The method convertProspectIds(Map) in the type CompassADFAdaptor is not applicable for the arguments (List)
This is in reference to this code, which is where the values should be passed into, and then formatted as the XML text:
public ArrayList<ProspectId> convertProspectIds(
Map<String, String> recipientSettings) {
ArrayList<ProspectId> prospectIdList = new ArrayList<ProspectId>();
int i = 0;
Iterator it = recipientSettings.entrySet().iterator();
while(it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
ProspectId Id = new ProspectId();
Id.setSequence(i++);
Id.setSource(ProspectIdSource.valueOf(pairs.getKey().toString()));
Id.setValue(pairs.getValue().toString());
prospectIdList.add(Id);
}
return prospectIdList;
}
I am not quite sure what I am missing ... can someone help me out?
convertProspectIds takes Map as an argument and you are passing it a List. You need to either change the method signature or convert List to Map.
The same goes for setProspect method. It does not expect ArrayList as a parameter.
Is there any way to convert from toString back to the object in Java?
For example:
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("value1", "test1");
myMap.put("value2", "test2");
String str = myMap.toString();
Is there any way to convert this String back to the Map?
Short answer: no.
Slightly longer answer: not using toString. If the object in question supports serialization then you can go from the serialized string back to the in-memory object, but that's a whole 'nother ball of wax. Learn about serialization and deserialization to find out how to do this.
No there isn't.
toString() is only intended for logging and debug purposes. It is not intended for serialising the stat of an Object.
Nope. Beside parsing this string returned by myMap.toString() and puting parsed values back into the map. Which doesn't seem to complicated here, since you have only Strings in your Map, so the output of myMap.toString() should be quite readable/parseable.
But in general this is not a great idea. Why to you want to do that?
A string is also an object. And no it's not possible to get the original object from its string representation (via toString()). You can simply get this by thinking about how much information is (or can be) stored within an object but how short the string representation is.
imposible if you think you can 'cast' the string;
try parsing; something like:
public static Map<String,String> parseMap(String text) {
Map<String,String> map = new LinkedHashMap<String,String>();
for(String keyValue: text.split(", ")) {
String[] parts = keyValue.split("=", 2);
map.put(parts[0], parts[1]);
}
return map;
}
Did you consider writing your own version to conversion utilities?
String mapToString(HashMap<String,String> map)
HashMap<String,String> stringToMap(String mapString)
public class CustomMap<K, V> extends HashMap<K, V>{
public String toString(){
//logic for your custom toString() implementation.
}
}
Have a class that extends HashMap and override the toString() method. Then you can do the following and achieve what you want to,
CustomMap<String, String> myMap = new CustomMap<String, String>();
myMap.put("value1", "test1");
myMap.put("value2", "test2");
String str = myMap.toString();