If I have the following yaml structure, how can I read it in java efficiently? Currently, I an reading as
xmas-fifth-day:
calling-birds:
value: four
partridges:
count: 1
value: "a pear tree"
turtle-doves:
value: two
Yaml yaml = new Yaml();
InputStream inputStream = new FileInputStream(new File("src/main/resources/customer.yaml"));
Map<String, Object> data = yaml.load(inputStream);
LinkedHashMap<String, LinkedHashMap<String, LinkedHashMap<String, String>>> lhm = (LinkedHashMap<String, LinkedHashMap<String, LinkedHashMap<String, String>>>) data.get("xmas-fifth-day");
Since I am using 3 levels of hashmap, just for the readability purpose, I was wondering if there's a simplified version of this yaml structure to read in less nesting LHM way in java?
EDIT: I don't want to add a POJO here since the config is meant to be dynamic in my use case, where any one can add any config and the code should work without editing the java classes.
It looks like you are using SnakeYAML so you could define custom types:
public class XmasFifthDay {
private CallingBirds callingBirds;
private Partridges partridges;
private TurtleDoves turtle-doves;
// getters and setters
}
public class CallingBirds {
private String value;
// getters and setters
}
// other classes: Partridges, TurtleDoves
and then load it with:
XmasFifthDay data = yaml.load(inputStream);
Perhaps CallingBirds, Partridges, TurtleDoves could be a single class with count and value fields but the example you have provided is not very clear.
As I see it you have better options with Jackson:
Yaml yaml = new Yaml();
InputStream inputStream = new FileInputStream(new File("test.yml"));
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
final JsonNode jsonNode = mapper.readValue(inputStream, JsonNode.class);
System.out.println(jsonNode.toPrettyString());
and the output will be :
{
"xmas-fifth-day" : {
"calling-birds" : {
"value" : "four"
},
"partridges" : {
"count" : 1,
"value" : "a pear tree"
},
"turtle-doves" : {
"value" : "two"
}
}
}
System.out.println(jsonNode.get("xmas-fifth-day"));
{"calling-birds":{"value":"four"},"partridges":{"count":1,"value":"a pear tree"},"turtle-doves":{"value":"two"}}
Also you could have a map of String and JsonNode, using TyperReference
inputStream = new FileInputStream(new File("test.yml"));
final Map<String,JsonNode> map = mapper.readValue(inputStream, new TypeReference<Map<String, JsonNode>>() {});
System.out.println(map.get("xmas-fifth-day"));
and the output is :
{"calling-birds":{"value":"four"},"partridges":{"count":1,"value":"a pear tree"},"turtle-doves":{"value":"two"}}
Related
Im trying to get a key:value pair from a simple jsonString to add it after into a memory tab. If facing an issue cause my input is a string. and it looks like my loop isnot able to read the key value pair.
I read many topics about it, and im still in trouble with it. As you can see below
{"nom":"BRUN","prenom":"Albert","date_naiss":"10-10-1960","adr_email":"abrun#gmail.com","titre":"Mr","sexe":"F"}
and my method, find only on object... the result is the same in my loop
public static ArrayHandler jsonSimpleObjectToTab(String data) throws ParseException {
if( data instanceof String) {
final var jsonParser = new JSONParser();
final var object = jsonParser.parse(data);
final var array = new JSONArray();
array.put(object);
final var handler = new ArrayHandler("BW_funct_Struct");
for( KeyValuePair element : array) {
handler.addCell(element);
Log.warn(handler);
}
return handler;
} else {
throw new IllegalArgumentException("jsonSimpleObjectToTab: do not support complex object" + data + "to Tab");
}
}
i also tryed before to type my array as a List, Object etc, without the keyValuePair object, i would appreciate some help.
Thanks again dear StackOverFlowers ;)
You can try this :
const json = '{"nom":"BRUN","prenom":"Albert","date_naiss":"10-10-1960","adr_email":"abrun#gmail.com","titre":"Mr","sexe":"F"}';
map = new Map();
const obj = JSON.parse(json,(key,value) => {
map.set(key,value)
});
and you'll have every pair stored in map
Simply split the whole line at the commas and then split the resulting parts at the colon. This should give you the individual parts for your names and values.
Try:
supposing
String input = "\"nom\":\"BRUN\",\"prenom\":\"Albert\"";
then
String[] nameValuePairs = input.split(",");
for(String pair : nameValuePairs)
{
String[] nameValue = pair.split(":");
String name = nameValue[0]; // use it as you need it ...
String value = nameValue[1]; // use it as you need it ...
}
You can use TypeReference to convert to Map<String,String> so that you have key value pair.
String json = "{\"nom\":\"BRUN\",\"prenom\":\"Albert\",\"date_naiss\":\"10-10-1960\",\"adr_email\":\"abrun#gmail.com\",\"titre\":\"Mr\",\"sexe\":\"F\"}";
ObjectMapper objectMapper = new ObjectMapper();
TypeReference<Map<String,String>> typeReference = new TypeReference<Map<String, String>>() {
};
Map<String,String> map = objectMapper.readValue(json, typeReference);
I just answered a very similar question. The gist of it is that you need to parse your Json String into some Object. In your case you can parse it to Map. Here is the link to the question with my answer. But here is a short version: you can use any Json library but the recommended ones would be Jackson Json (also known as faster XML) or Gson(by Google) Here is their user guide site. To parse your Json text to a class instance you can use ObjectMapper class which is part of Jackson-Json library. For example
public <T> T readValue(String content,
TypeReference valueTypeRef)
throws IOException,
JsonParseException,
JsonMappingException
See Javadoc. But also I may suggest a very simple JsonUtils class which is a thin wrapper over ObjectMapper class. Your code could be as simple as this:
Map<String, Object> map;
try {
map = JsonUtils.readObjectFromJsonString(input , Map.class);
} catch(IOException ioe) {
....
}
Here is a Javadoc for JsonUtils class. This class is a part of MgntUtils open source library written and maintained by me. You can get it as Maven artifacts or from the Github
Consider the following code:
public void testDumpWriter() {
Map<String, Object> data = new HashMap<String, Object>();
data.put("NAME1", "Raj");
data.put("NAME2", "Kumar");
Yaml yaml = new Yaml();
FileWriter writer = new FileWriter("/path/to/file.yaml");
for (Map.Entry m : data.entrySet()) {
String temp = new StringBuilder().append(m.getKey()).append(": ").append(m.getValue()).toString();
yaml.dump(temp, file);
}
}
The output of the above code is
'NAME1: Raj'
'NAME2: Kumar'
But i want the output without the single quotes like
NAME1: Raj
NAME2: Kumar
This thing is very comfortable for parsing the file.
If anyone have solution, please help me to fix. Thanks in advance
Well SnakeYaml does exactly what you tell it to: For each entry in the Map, it dumps the concatenation of the key, the String ": ", and the value as YAML document. A String maps to a Scalar in YAML, and since the scalar contains a : followed by space, it must be quoted (else it would be a key-value pair).
What you actually want to do is to dump the Map as YAML mapping. You can do it like this:
public void testDumpWriter() {
Map<String, Object> data = new HashMap<String, Object>();
data.put("NAME1", "Raj");
data.put("NAME2", "Kumar");
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(options);
FileWriter writer = new FileWriter("/path/to/file.yaml");
yaml.dump(data, writer);
}
I have the Yaml file:
#Define CDN domains
---
CDN:
quality: 200..300
cost: low
Video-type: mp4
and with this Java code, I retrieve sub values of CDN:
// The path of your YAML file.
Yaml yaml = new Yaml();
Map<String, Map<String, String>> values =
(Map<String, Map<String, String>>) yaml
.load(new FileInputStream(new File("/workspace/servlet-yaml/src/test.yaml")));
for (String key : values.keySet()) {
Map<String, String> subValues = values.get(key);
for (String subValueKey : subValues.keySet()) {
System.out.println(values);
}
}
The output is:
{CDN={quality=200..300, cost=low, Video-type=mp4}}
{CDN={quality=200..300, cost=low, Video-type=mp4}}
{CDN={quality=200..300, cost=low, Video-type=mp4}}
First of all, I don't know why it repeats three times?
Secondly, I want to write a code that
if cost = low , then do somthing.
First of all, I dont know whay it reapets three times?
Because you tell it to. For each subValueKey, print the whole value set. There are three sub-keys, so the complete value set gets printed three times.
Secondly, I want to write a code that if cost = low , then do somthing.
Yaml yaml = new Yaml();
Map<String, Map<String, String>> values =
(Map<String, Map<String, String>>) yaml.load(
new FileInputStream(new File(
"/workspace/servlet-yaml/src/test.yaml")));
final Map<String, String> cdn = values.get("CDN");
// or iterate over all keys like you currently do
final String cost = cdn.get("cost");
// or iterate over all subkeys and compare them to "cost".
// that way, it's easier to handle missing keys.
if ("low".equals(cost)) {
// do something
}
I've got a YAML file that looks like this:
---
name:
storage:
documentfiles:
username: rafa
password: hello
And I'm trying to get the last two username and password values. My current code is the one below. I'm using a Map to store the YAML values, but since there is more than one child when I map.get() anything past name it gives me a null value. if I do map.get(name) I get {storage={documentfiles={username=rafa, password=hello}}} Does anyone know how I can correctly get the username and password?
public Map grabYaml(){
Yaml reader = new Yaml();
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(yamlFileName);
Map map = (Map) reader.load(inputStream);
return map;
}
Something like this
public class Test {
public Map grabYaml() throws IOException {
Yaml reader = new Yaml();
InputStream inputStream = new FileInputStream(new File(yamlFileName));
Map map = (Map) reader.load(inputStream);
return map;
}
public static void main(String[] args) throws IOException {
Map storage = (Map) new Test().grabYaml().get("name");
Map documentfiles = (Map)storage.get("storage");
Map userData = (Map) documentfiles.get("documentfiles");
System.out.println(userData.get("username"));
System.out.println(userData.get("password"));
}
}
I am testing Avro for java with a simple record composed of a string and a map. Here's my schema:
{
"type":"record",
"name":"TableRecord",
"fields":[
{"name":"ActionCode","type":"string"},
{
"name":"Fields",
"type":{"type":"map","values":["string","long","double","null"]}
}
]
}
And here's a very simple test case that fails:
#Test
public void testSingleMapSerialization() throws IOException {
final String schemaStr; // see above
// create some data
Map<String, Object> originalMap = new Hashtable<>();
originalMap.put("Ric", "sZwmXAdYKv");
originalMap.put("QuoteId", 4342740204922826921L);
originalMap.put("CompanyName", "8PKQ9va3nW8pRWb4SjPF2DvdQDBmlZ");
originalMap.put("Category", "AvrIfd");
// serialize data
Schema.Parser parser = new Schema.Parser();
Schema schema = parser.parse(schemaStr);
ByteArrayOutputStream out = new ByteArrayOutputStream();
DatumWriter<GenericRecord> writer = new GenericDatumWriter<>(schema);
Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
GenericRecord datum = new GenericData.Record(schema);
datum.put("ActionCode", "R");
datum.put("Map", originalMap);
writer.write(datum, encoder);
encoder.flush();
out.flush();
// deserialize data
DatumReader<GenericRecord> reader = new GenericDatumReader<>(schema);
Decoder decoder = DecoderFactory.get().binaryDecoder(out.toByteArray(), null);
datum = new GenericData.Record(schema);
Map<String, Object> deserializedMap = (Map<String, Object>) reader.read(datum, decoder).get("Map");
System.out.println(originalMap);
System.out.println(deserializedMap);
Assert.assertEquals("Maps data don't match", originalMap, deserializedMap);
}
And here's the output of the test:
{CompanyName=8PKQ9va3nW8pRWb4SjPF2DvdQDBmlZ, Ric=sZwmXAdYKv, Category=AvrIfd, QuoteId=4342740204922826921}
{QuoteId=4342740204922826921, Category=AvrIfd, CompanyName=8PKQ9va3nW8pRWb4SjPF2DvdQDBmlZ, Ric=sZwmXAdYKv}
java.lang.AssertionError: Maps data don't match expected:<{CompanyName=8PKQ9va3nW8pRWb4SjPF2DvdQDBmlZ, Ric=sZwmXAdYKv, Category=AvrIfd, QuoteId=4342740204922826921}> but was:<{QuoteId=4342740204922826921, Category=AvrIfd, CompanyName=8PKQ9va3nW8pRWb4SjPF2DvdQDBmlZ, Ric=sZwmXAdYKv}>
As you can see, the two maps look identical but the test fails. JUnit calls the "equals" method under the covers, and that should return true. BTW, if you're wondering what's the gibberish, I usually create test cases with randomly generated data, so that's where it comes from.
Am I doing something wrong? Is there a catch with string serialization/de-serialization I'm not aware of? I searched online with no success.
Ideas?
Thanks
Giodude
I figured out what the "catch" was. I was comparing a map containing java.lang.Strings with one containing org.apache.avro.util.Utf8. It turns out the Utf8 equals method doesn't work if used with strings. I realized this by adding to my test case the following:
for (Object o : deserializedMap.values())
System.out.println(o.getClass());
for (Object o : deserializedMap.keySet())
System.out.println(o.getClass());
which prints the following:
class java.lang.Long
class org.apache.avro.util.Utf8
class org.apache.avro.util.Utf8
class org.apache.avro.util.Utf8
class org.apache.avro.util.Utf8
class org.apache.avro.util.Utf8
class org.apache.avro.util.Utf8
class org.apache.avro.util.Utf8
I guess this was to be expected since Avro always converts strings to its native Utf8 type. I assumed it would reproduce my map as-is, but that's not the case. It's odd that the type cast to Map succeeded, I am not clear on how that happened.
Yes, Avro map uses org.apache.avro.util.Utf8 as default key since 1.5, and could be changed to String. For more details, you may refer: https://issues.apache.org/jira/browse/AVRO-803 or Apache Avro: map uses CharSequence as key