How to verify param is actually Json, not just a string - java

I have a JsonNode that I am accepting as a param to an endpoint. Looks something like this:
#ApiModelProperty(value = "data", example = "{}", required = true)
#NotNull(message = "data cannot be null")
protected JsonNode data;
I want to create a Validator called #ValidJson. The JsonNode itself will make sure that the information being fed to it is valid json, so if I pass something like:
{"data" = "hello",}, where there is an extra comma after the "hello", it will throw an error. But how do I make sure I'm not getting something like "Hello" for JsonNode, which is valid json since a normal String is a valid json representation?
I was thinking at first of checking if the String is just alphanumeric and if it is, then I would consider it invalid, but then obviously someone can just pass a string with a symbol in it and it would be fine. The best solution I could think of was to check that the first and last characters are { and } respectively, and JsonNode would take care of the rest. But I don't know enough about JsonNode, so maybe someone here has a better idea?
Edit:
To be more specific with what I want, here are a couple of examples:
JsonNode will take care of incorrect Json. I want to do some stricter verification on the data I get in. I don't want to receive any sort of Json, I want the "real", serializable json that we all mean when we say json. If a user passes a string that looks like:
"Hello"
"Hello world"
"I'm just a random String that isn't in a key:value structure"
Etc, I want to throw an error and ask them for Json formatted in key:value structure like:
{
"key": "value"
}
I was thinking of checking to see if the first and last string values are { and } respecitvely, as I mentioned in my comments, but I think that's too "hacky" and there could perhaps be a better method.

Jackson is designed to convert JSON into Java Objects. Although your usecase could work, it is most likely not very helpful. Take a look into https://www.baeldung.com/jackson this tutorial may help you to understand Jackson and its JSON conversion.
If you really want to use a JsonNode, Jackson would already check if it's a proper JSON, otherwise it would immediately throw an Exception because it can't parse it into a JsonNode.
If you want a JSON Object you could simply use a Map<String, Object> instead of JsonNode. This will handle the key-value part.

As #VinceEmigh pointed out in the comments, isObject is the function I was looking for.

Related

How does Gson.fromJson method succeeded to parse a Json string although TypeToken isn't suit to parsed String?

I have a code that I can't understand why it isn't throwing a run time exception.
Here is a Json String that I'm trying to convert to Map<String, Any>, but some of the keys are from Int type and not a String.
The code:
val json = """{1: "Kotlin Tutorial", "2": "bezkoder", "3" : ["Kotlin","Basic"]}"""
val gson = Gson()
var tutorialMap: Map<Int, Any> = gson.fromJson(json, object : TypeToken<Map<Int, Any>>() {}.type)
tutorialMap.forEach { Log.e("dadffas",it.toString()) }
As you can see keys: 1,3 are from Int type. What happen here? some kind of boxing?
In addition if I declare the TypeToken as Map<Int,Any> code is still being compiled? Again kind of boxing (behind the scene String.toInt method is running)?
Gson's approach is, to come from the Java/Kotlin side and try to coerce the Json value into the type that is expected on Java side.
So, as the type of the keys of your map is Integer, it tries to coerce what it gets from Json to Integer.
You need to be aware that your whole Json initally is just a string to Gson. It gets tokenized but is still a bunch of strings until in the end Gson sees you want a certain string as an Integer. And it ia clever enough to turn 2 and "2" into an integer.
If your json would have a key like "foo", you'd get an exception.
P.S: If you're really interested you can use the debugger and step into the fromJson() method and see exactly how its done.

Parsing JSON string and preserving key-data order when using json-simple without manually constructing HashMap

I know that this topic has been talked about, and the use of a LinkedHashMap is a 'hacky' way to maneuver this, but if I'm given thousands of JSON strings as input, and eventually want to output them back in their original form, is there anyway to preserve the order without manually constructing LinkedHashMaps.
For example a string like this
{"key":1,"surname":"Reed","given":"Ryan","address":{"state":"CA","postal":"90210"},"gender":"M"}
Right now if I parse the object like so:
JSONObject jsonObject = (JSONObject) parser.parse(str);
System.out.println(jsonObject);
My output will look like this:
{"surname":"Reed","gender":M,"address":{"postalCode":"90210","state":"CA"},"key":1,"given":"Ryan"}
Is there anyway I can get the output to match exactly like the given input?
In Json property structure, order does not matter. but if you have specific order in your mind you can use Jackson to order them in you desirable way, both in your server and client apps.
https://www.baeldung.com/jackson
http://www.davismol.net/2016/10/24/jackson-json-using-jsonpropertyorder-annotation-to-define-properties-serialization-order/
I think it is impossible by default.
You can refer to this RFC https://www.ietf.org/rfc/rfc4627.txt
An array is an ordered sequence of zero or more values.
If you want to hack it you can override the data structure and use the data structure which preserves the order.

java objectmapper readvalue reading wrong string

I need to convert a certain JSON string to a Java object. I am using Jackson ObjectMapper for reading the JSON. The JSON String is something like this:-
"{"emailId":"gmail#rajnikant.com","accessToken":"accTok"}4".
When I am using objectMapper.readValue() for reading the JSON string to a specific destination class, it should throw an exception because of the JSON string being appended by 4. What should I do so that only valid JSON can be read and in other cases it will throw an exception?
To Jackson, GSON and others, a JSON string with some characters appended after the last } is valid JSON as long as what is contained between the {} is valid JSON.
As stated by a member of FasterXML (Jackson) team:
Yes. This is by design. If you want to catch such problems, you need to construct JsonParser, advance it manually. Existence of multiple root-level values is not considered a validity problem.
Reference: https://github.com/FasterXML/jackson-databind/issues/726
So if you need to enforce "clean" JSON you'll have to extend the default parser with your own functionality. However, IMO if it's OK to the default parser it should be OK to you too (unless we're dealing with some inter-language incompatibility scenario here).

Can JSON field not be a string?

As far as I know, all JSON field names are string values. However, I encountered a code snippet that does a string check on the "keys" of JSON to see if it's a string, and if not, it throws an exception. It goes something like:
if (!(key instanceof String)){
throw new exception();}
Is this check necessary?
EDIT:
For example,
while (jp.nextToken() == JsonToken.FIELD_NAME){
String key = jp.getCurrentName();
}
This code snippet will only progress to JSON tokens that are strings, so I was wondering if a JSON could contain fieldnames that are not strings so that Jackson parser will simply skip those fieldnames.
From the JSON official website (and by proxy, the JSON Data Interchange Standard):
When creating an object, the key must be a String.
EDIT: As #SotiriosDelimanolis pointed out in the comments, this only applies to the format of the JSON file, not necessarily once parsed through a Java library.
Jackson, for example, can deserialize keys into custom types - #SotiriosDelimanolis

Best Way To Test For Correct JSON Serialization

I use the Jackson library with Java to serialize POJOs to JSON and vice versa. Let's say that I am running some tests where I am serializing an object and I know that the expected JSON string is {"firstName":"John", "lastName":"Doe"}. What is the best way to validate that my object serialized to the above string? Or better yet, what is the best way to validate that each of the fields is what I expect?
I have tried simply hard coding that string in and doing a comparison, but I have had cases where I serialize to JSON -> deserialize to POJO -> then serialize the deserialized POJO back to JSON again, and the fields are out of order. Then the string comparison fails even though all of the fields are correct.
Is there are better/different way to verify that my JSON string has the expected fields in it when testing?
I ran into the exact same situation as you and found JSONassert to be very helpful.
In your JUnit test you would have to add something like this:
String actual = getSerializedContent();
String expected = "{firstName:\"John\", lastName:\"Doe\"}";
JSONAssert.assertEquals(expected, actual, false);

Categories

Resources