Edit yaml in a precise way - java

I would like to edit a yaml in java , ( I'm editing BungeeCord config file to create a system that launches instance of bungeecord with a defined by user port ) but in a precise way , i need to write exactly this in the yaml file :
listeners:
- query_port: 25577
motd: '&1Another Bungee server'
tab_list: GLOBAL_PING
query_enabled: false
proxy_protocol: false
ping_passthrough: false
priorities:
- lobby
bind_local_address: true
host: 0.0.0.0:25577
max_players: 1
tab_size: 60
force_default_server: false
I did something very similar but there is a vertical bar that prevents BungeeCord from reading the file :
public class ProxyYaml {
HashMap<String, Object> entries = new HashMap<String, Object>();
public ProxyYaml() {
entries.put("query_port", 25577);
entries.put("motd", "Hey Guys");
entries.put("tab_list", "GLOBAL_PING");
entries.put("query_enabled", false);
entries.put("proxy_protocol", false);
entries.put("ping_passthrough", false);
entries.put("priorities", Arrays.asList("lobby"));
entries.put("bind_local_address", true);
entries.put("host", "0.0.0.0:25577");
entries.put("max_players", 1);
entries.put("tab_size", 60);
entries.put("force_default_server", false);
}
public ArrayList<String> getProperties() {
ArrayList<String> finalString = new ArrayList<String>();
for(String entry : entries.keySet()) {
finalString.add(entry + ": " + entries.get(entry).toString());
}
return finalString;
}
}
( I'm using SimpleYaml api but I can change the api if needed )
File propsProxyFile = new File(path + "config.yml");
YamlFile propsProxyYaml = new YamlFile(propsProxyFile);
try {
propsProxyYaml.load(propsProxyFile);
propsProxyYaml.set("listeners", Arrays.asList(new ProxyYaml().getProperties()));
propsProxyYaml.save(propsProxyFile);
} catch (IOException | InvalidConfigurationException e) {
System.out.println(MainNetwork.logo + "Can't load proxy properties file");
return;
}
There is the code output ( with the vertical bar ) :
listeners:
- |
query_port: 25577
motd: '&1Another Bungee server'
tab_list: GLOBAL_PING
query_enabled: false
proxy_protocol: false
ping_passthrough: false
priorities:
- lobby
bind_local_address: true
host: 0.0.0.0:25577
max_players: 1
tab_size: 60
force_default_server: false
What should I do please ?

The pipe character (|) starts a YAML block scalar. It means that all the following lines are a literal string and not subject to further YAML parsing.
There are lots of strange things happening in your code, let's go over them:
public ArrayList<String> getProperties() {
ArrayList<String> finalString = new ArrayList<String>();
for(String entry : entries.keySet()) {
finalString.add(entry + ": " + entries.get(entry).toString());
}
return finalString;
}
You are manually transforming a mapping into a list of strings here. Why do you do that? You expect the final YAML file to contain the key-value pairs as mapping, so you should not transform them into a list of strings.
Let's discuss what happens here with a quick example:
Assume we have this java Map:
Map<String, String> value = Map.of("foo", "bar");
If we directly serialize this to YAML, we would get
foo: bar
but if we pipe it through your method, we'll get
- "foo: bar"
i.e. a sequence of one scalar value – because we manually transformed the mapping entry into a string! This is not what you want.
propsProxyYaml.set("listeners", Arrays.asList(new ProxyYaml().getProperties()));
You call Arrays.asList on the return value of getProperties() which is of type ArrayList<String> so asList will return a value of type List<ArrayList<String>>, which has a single entry that is the list you built. It is unclear why you call Arrays.asList unless you want to have a list of lists. According to the desired YAML output, this is not what you want.
Now let's discuss what the set method does. I don't really know SimpleYAML and frankly, its documentation is horrible as it basically only consists of the autogenerated API docs. The first parameter of the method is named path, which implies that it is not a simple mapping key.
What apparently happens is that the List<ArrayList<String>> value is transformed into a YAML sequence with one scalar value, and that scalar value contains all the string values you produced, separated by newlines. Without proper documentation, it is impossible to say whether this is expected behavior or a bug. In any case, it makes no sense.
Now the actual YAML contains a mapping at its root with one entry, whose key is the scalar listeners. Its value is a list that contains another mapping. This means, the type you actually want to serialize is
Map<String, List<Map<String, Object>>>
I suggest that you simply build a value of this type and use the SnakeYAML API, which does have proper documentation on how its serialization system works. SimpleYAML uses SnakeYAML under the hood anyway, and there seems to be no reason to use a poorly documented API with surprising behavior instead of a well-documented one.
You can also create a custom Java class instead of using Maps. Then the keys would become class fields.

Related

How to map Java HashMap<String, Pojo> variableName to typescript

I am trying to figure out how to create Typescript interface for HashMap<String, Pojo> I have tried the below variations but:
export interface ServerResponse {
// forWhomAdd: Array<ForWhomAdd>; // size/length is undefined
// forWhomAdd: Map<string, Address>; // size/length is undefined
forWhomAdd: { [firstLastName: string]: Address }; // How do I access keys and values?
}
I'd prefer not to do this the third way because I'm loosing my types which I'm trying to define.
This is how it looks when I log it:
{testKey: {property: value, prop: value}}
So I can get the Object by:
let serRes: ServerResponse = data;
serRes.testKey // gives back the object
But I don't know keys and I need to loop over serRes.
Assuming your data has list of ServerResponse.
let serRes: Array<ServerResponse> = data;
But I don't know keys and I need to loop over serRes.
you can get the values of the Object with keys() method of Objectclass.
Object.keys(this.serRes).map(key => this.serRes[key])
UPDATE :
I have created stackblitz for your case check it here.

How to add list of items to an ArrayList<String> in java?

I have list of words which I need to load to ArrayList< String >
prefix.properties
vocab\: = http://myweb.in/myvocab#
hydra\: = http://www.w3.org/ns/hydra/core#
schema\: = http://schema.org/
"vocab:" is actually "vocab:" .Slash(\) is used to read colon(:) character because it is special character.
Dictionary.java
public class Dictionary {
public static ArrayList<String> prefix = new ArrayList<>();
static {
Properties properties = new Properties();
InputStream input = null;
input = ClassLoader.getSystemResourceAsStream("prefix.properties");
System.out.println(input!=null);
try {
properties.load(input);
} catch (IOException e) {
e.printStackTrace();
}
Set<Map.Entry<Object, Object>> entries = properties.entrySet();
for(Map.Entry<Object, Object> E : entries)
{
prefix.add(E.getKey().toString());
prefix.add(E.getValue().toString());
}
}
}
In Dictionary.java , ArrayList prefix will have
prefix = [
"vocab:",
"http://myweb.in/myvocab#",
"hydra:",
"http://www.w3.org/ns/hydra/core#",
"schema:",
"http://schema.org/"
]
I am querying some data in another class.
For eg:
public class QueryClass
{
public ArrayList<String> queryResult(String findKey)
{
ArrayList<String> result = new ArrayList<String>();
ArrayList<String> prefix = Dictionary.prefix;
Iterator<String> iterator = prefix.iterator();
while (iterator.hasNext())
{
String currentKey = iterator.next()+findKey;
/**
Here my logic to search data with this currentKey
*/
}
return result;
}
}
Problem :
I want to avoid this method to load from .properties file because there is possibility of odd number of elements can be present while .properties file provide (key,value) pair way to store data.
Reason why I have to load from separate file ? Because In future I will have to add more keywords/String thats why I put it in prefix.properties file.
Is there any alternative way to do this?
Do not re-invent the wheel.
If you can define the file format, then just go for java properties.
You see, the Properties class has a method getProperty(String, String) where the second argument can be used to pass a default value for example. That method could be used in order to fetch keys that don't come with values.
I would be really careful about inventing your own format; instead I would look into ways of re-using what is already there. Writing code is similar to building roads: people forget that each new road that is built translates to maintenance efforts in the future.
Besides: you add string values to a list of strings by calling list.add(strValue). That is all that is to that.
Edit on your comment: when "java properties" are not what you are looking for; then consider using other formats. For example you could be persisting your data in some JSON based format. And then just go for some existing JSON parser. Actually, your data almost looks like JSON already.
I'm not sure why you need to use ArrayList but if you want to pass these property keys/values, there are 2 better ways:
Use Properties itself.
Convert to HashMap.

String value from properties file

I have a properties (cant change this file) and it looks like:
aaa.bbb.ccc.first=my first value
aaa.bbb.ccc.second=my second value
aaa.bbb.ccc.third=my third value
If I need any value in java classes I use i18n.getText("aaa.bbb.ccc.first") but it works only for single value.
Problem is because I dont know:
-value's names
-how many values are in aaa.bbb.ccc.~
How is it possible to get list of value aaa.bbb.ccc~?
You could use a MapFilter. Use the MapFilter(Properties p, String prefix) constructor.
public void test() {
Properties props = new Properties();
props.put("aaa.bbb.ccc.first", "my first value");
props.put("aaa.bbb.ccc.second", "my second value");
props.put("aaa.bbb.ccc.third", "my third value");
props.put("Other.props", "others");
MapFilter<String> filtered = new MapFilter(props, "aaa.bbb.ccc.");
for (Map.Entry<String, String> e : filtered.entrySet()) {
System.out.println("Key: " + e.getKey() + " Value: " + e.getValue());
}
System.out.println(filtered);
}
Hash maps are not meant for the kind of lookup you want to do: Tries and radix trees are. There is an implementation of the Patricia trie data structure (i.e., binary radix trees) in Apache Commons Collections: Just create a trie from your Map<String, Whatever> (you have a nice constructor at the purpose) and with prefixMap("aaa.bbb.ccc") you obtain the submap of all the entries whose key have that prefix.
Properties has a method propertyNames(). You can use that to get all the keys then do whatever you want from there.

What is the reasoning on modelling a class to represent JSON data and do I need to?

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.

How can i get the property in the sequence they have in file

I am using file properties file and loop through that file.
Properties Config= new Properties();
Config.load(new FileInputStream("c:\\Config.properties"));
Enumeration e = Config.propertyNames();
while (e.hasMoreElements())
{ String key = (String) e.nextElement();
System.out.println(key + " -- " + Config.getProperty(key));
}
My Input in C:\Config.properties is
Input
fun1= abc
fun2=pqr
fun3 mno
Output
fun2 -- pqr
fun1 -- abc
fun3 -- mno
How can i get this what exactly in the Config file.
That's not possible using java.util.Properties as it extends java.util.Hashtable which is by nature unordered.
Your best bet is reading and parsing it yourself using for example a BufferedReader or a Scanner.
I however wonder why ordering is that important. There may be alternatives which I can only suggest if you elaborate the functional requirement behind this all in detail. Based on the given example, the closest what I can suggest you is the following:
List<String> funs = new ArrayList<String>();
for (int i = 1; i < Integer.MAX_VALUE; i++) {
String fun = config.getProperty("fun" + i);
if (fun == null) break;
funs.add(fun);
}
// ...
Properties is not an ordered map so you might want to create a SortedMap from the loaded properties. This guarantees that the keys are naturally ordered. But i assume you want the order to be the one that you specify in the properties file. There is no way to do it automatically. You need to code it yourself.
As BalusC suggested read line by line from beginning using file API, create a Pair/Tuple object and insert it in the List object or add it to SortedMap based upon your usage requirement
I would be shocked if there isn't a library that already does this. Try Apache Configuration, for example.

Categories

Resources