Retain line number after parsing YAML to JSON - java

I receive a YAML file which I parse using snakeyaml-engine(for YAML 1.2).
LoadSettings settings = new LoadSettingsBuilder().build();
Load load = new Load(settings);
Map<String, Object> yamlMap = (Map<String, Object>) load.loadFromString(new String(<yaml_contents_as_byte_array>));
I then use GSON to convert this into a JsonObject.
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(gson.toJson(yamlMap)).getAsJsonObject();
The goal is to run through this YAML, checking it against a known data model. If fields differ, required fields are missing, etc. I must return an error for each one. The goal is to find some way to include the line number of the problem field from the original YAML, but as I am doing my validations via JSON, I do not know the original line number when I run into an error. I am curious if there is some straightforward way to persist the line number of the YAML, perhaps by adding a lineNumber/columnNumber value to each field as it is parsed and inserted into the map?

A distinct non-answer: I am not aware of any build in mechanism that you could use here. So you probably have to write code yourself to achieve to do both things:
keep track of yaml properties together with line numbers
enhance the corresponding Json beans with line number fields
But honestly, the real answer (imho): have distinct validation code that can work on either yaml or Json input "directly". That additional conversion for the purpose of validation, I find that highly doubtful.

Related

Is it a way to configure Jackson ObjectMapper to allow True/False instead true/false?

I have a malformed JSON with "key":True instead of "key":true
So I'm getting the following error :
"com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'True': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')"
I can solve my problem with replace("True", "true") on string before to parsing the JSON string.
But I would like to do this automatically with handling error or something like that. Using Jackson config. Is it possible?
private static final ObjectMapper jsonMapper;
JsonFactory f = JsonFactory.builder()
.enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS)
.enable(JsonReadFeature.ALLOW_SINGLE_QUOTES)
.build();
jsonMapper = JsonMapper.builder(f).build();
JsonNode res = jsonMapper.readTree(content)
JSON-sample:
{
...,
"key" : True,
...
}
What you want is inherently impossible.
The parser needs to be able to distinguish between the tokens in order to know how to deal with the next portion of JSON because different JSON-elements should be treated in a different way.
There are only two valid non-String (not enclosed in quotations marks) boolean values: true and false (and null also would be successfully parsed as false). And parsing algorithm depends on them, because it needs to be able to recognize the tokens.
boolean values represented as String, i.e. enclosed in quotations marks, are parsed in the case-insensitive manner automatically without any customization. And for non-String boolean values no formatting features which allow to tweak the behavior of the parser exist.
I'm afraid that there's no other way to solve the problem other then preprocess the JSON adjusting boolean values.
It seems you already know the answer.
You have malformed JSON, and malformed JSON IS NOT JSON.
Jackson ObjectMapper is a JSON processor.
If you want to know more detailed specification and implementation, please refer to the link below:
https://www.json.org/json-en.html
https://github.com/FasterXML/jackson-core/blob/jackson-core-2.14.1/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java#L741

Best way to parse JSON with an unknown structure for comparison with a known structure?

I have a YAML file which I convert to JSON, and then to a Java object using GSON. This will be used as the standard definition which I will compare other YAML files against. The YAML files which I will be validating should contain fields with identical structures to my definition. However, it is very possible that it might contain fields with different structure, and fields that don't exist within my definition, as it is ultimately up to the user to create these fields before I receive the file. A field in the YAML to be validated can look like this, with the option of as many levels of nesting as the user wishes to define.
LBU:
type: nodes.Compute
properties:
name: LBU
description: LBU
configurable_properties:
test: {"additional_configurable_properties":{"aaa":"1"}}
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 4
capabilities:
virtual_compute:
properties:
virtual_memory:
virtual_mem_size: 8096 MB
virtual_cpu:
cpu_architecture: x86
num_virtual_cpu: 2
virtual_cpu_clock: 1800 MHz
requirements:
- virtual_storage:
capability: capabilities.VirtualStorage
node: LBU_Storage
Currently, I receive this YAML file and convert it to a JsonObject with Gson. It is not possible to map this to a Java object because of any possible unknown fields. My goal is to run through this file and validate every single field against a matching one in my definition. If a field is present that does not exist in the definition, or does exist but has properties that differ, I need to inform the user with specific info about the field.
So far, I am going the route of getting fields like this.
for (String field : obj.get("topology_template").getAsJsonObject().get("node_template").getAsJsonObject().get(key).getAsJsonObject().get(
obj.get("topology_template").getAsJsonObject().get("node_templates").getAsJsonObject().get(key).getAsJsonObject().keySet().toArray()[i].toString()).getAsJsonObject().keySet()) {
However, it seems that this is rather excessive and is very hard to follow for some deeply nested fields.
What I want to know is if there is a simpler way to traverse every field of a JsonObject, without mapping it to a Java object, and without explicitly accessing each field by name?
I think you are looking for something like a streaming Json Parser:
Here's an example
String json
= "{\"name\":\"Tom\",\"age\":25,\"address\":[\"Poland\",\"5th avenue\"]}";
JsonFactory jfactory = new JsonFactory();
JsonParser jParser = jfactory.createParser(json);
String parsedName = null;
Integer parsedAge = null;
List<String> addresses = new LinkedList<>();
while (jParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jParser.getCurrentName();
if ("name".equals(fieldname)) {
jParser.nextToken();
parsedName = jParser.getText();
}
if ("age".equals(fieldname)) {
jParser.nextToken();
parsedAge = jParser.getIntValue();
}
if ("address".equals(fieldname)) {
jParser.nextToken();
while (jParser.nextToken() != JsonToken.END_ARRAY) {
addresses.add(jParser.getText());
}
}
}
jParser.close();
Please find the documentation here:
https://github.com/FasterXML/jackson-docs/wiki/JacksonStreamingApi

Parse JSON without knowing format (can be one of two different objects)

I have two concrete objects with a known schema (totally different). Then I get JSON from a client and want to map it into one of this object.
Is it possible to somehow check type before conversion, or I have to try to convert it into each of object and check if parsing was correct?
EDIT:
In example:
{"id":"1","name":"oneone"}
and second
{"age":50,"type":"elephant"}
Personally, I would parse the JSON using GSON or something similar and look for the key that is unique to one of the JSON formats, for instance "age". In reality, you could probably do this as a String as #user743414 mentioned as well.
UPDATE:
Here is some code to reflect what I'm talking about
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = jsonParser.parse(jsonString).getAsJsonObject();
Set<String> keys = jsonObject.keySet();
if(keys.contains("age")){
//Map to one object
} else {
//Map to the other object
}
If you are sure schema is constant for both JSON’s, then simply take a unique parameter like age in this example and check if it exists in the JSON.
If (String.contains(“age”)) {
//then it’s 2nd JSON
} else {
//then it’s 1st JSON
}

Parsing relaxed JSON without quotes Java

This source Google Finance option Chain Data returns the relaxed JSON, I wasn't able to Parse this JSON through PDI (Pentaho Data Integratio) (originally required) So thought of Parsing it in Java Code.
I tried using ObjectMapper and its Feature to allow unquoted field names but the json returned from above source is totally relaxed and can miss quotes anywhere.
String json = "{name:\"ankit\"}";
Map<String,String> map = new HashMap<String,String>();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(org.codehaus.jackson.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
map = mapper.readValue(json,new TypeReference<HashMap<String,String>>(){});
System.out.println(map);
It works fine if the keys in JSON are unquoted but fails if the same goes with values.
Is there any way out doing it with Pentaho Data integration or in Java Class.

How to store JSON array variable and printing specific value. (JAVA)

To print the neccesary details we used the following command
System.out.println(msgFromServer.data);
Now the following details are fetched from server
[{id={name=XDA Studio, color=red}, angle=-0.24456912236854822, piecePosition={pieceIndex=39.0, inPieceDistance=35.797426838065036, lane={startLaneIndex=1.0, endLaneIndex=1.0}, lap=2.0}}]
How to store these above server messages in json array variable and print value of angle alone.
msgFromServer.data seems to return a List. Your List is something like
List<Map<String,Object>>
If you use Jackson parser, you could probably convert it to a Json String using the following code:
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(list);
I have provided code for Jackson library, you could use any other with library specific implementation and code

Categories