Can JSON field not be a string? - java

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

Related

Gson cannot deserialize same string it created?

I have a HashMap<Picture, String> with Picture being a data-class I created in kotlin. I save the HashMap into the SharedPreferences using gson.toJson(hashmap) and this works fine. But when I try to deserialize the very same string (I checked) into the HashMap<Picture, String> again, it fails with a weird error.
This is the Exception:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 3 path $.
This is the string for reference:
{
"Picture(image_url\u003dhttps://nftmintapp.infura-ipfs.io/ipfs/QmZnbgRFCvqXeahD37vaRANjPiyF9oCC2aWw1TwHat8SaU, creator_name\u003dmarkus, creator_address\u003d0x0, image_name\u003dethOS3, additional_url\u003dhttps://google.com)":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
The String I save in reference to the String is a Bytearray that I convert to string using Base64.encodeToString(bytes, Base64.NO_WRAP).
I assumed that gson would be able to de-serialize anything it serialized itself, has anybody ever encountered this?
The JSON specification only allows strings as property names of JSON objects. Gson works around this by simply calling the toString() function on Map keys and serializing that as JSON property names. However, for deserialization it uses the standard JSON deserialization logic.
For simple Map keys such as String or Int this works just fine, but for complex key types, such as the Picture class in your case, this leads to undesired behavior.
The solution is to either
restructure your code so it uses a Map<String, ...> and for example stores the picture data as part of the Picture class as well, or have a data class which contains the picture data and the Picture object
or to
use GsonBuilder.enableComplexMapKeySerialization(), see also the user guide for additional information
In case you already have released a version of your app with this faulty JSON serialization logic and you want to preserve backward compatibility, you will have to write a JsonDeserializer (or TypeAdapterFactory, but that is a bit more complicated) which tries to deserialize this faulty JSON value.

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

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.

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).

Same JSON Strings when converting to json string cross platform

If I have a json String {"k":"v","a":"b"}.
If I convert it into a json Object and then back to String in Java let say using Gson library and store it in some database.
And Also I convert it into json Object and back to String in Python , it is possible that I get the String as {"a":"b","k":"v"} , though json object will be same but now I cannot do a string match as the order is changed.
How can I solve this problem ?
There is no guarantee that order of json object keys will be same.
Json object is unordered by specefication: http://json.org/
An object is an unordered set of name/value pairs.
If you want some order, you should use json array instead of json object.
An array is an ordered collection of values.
see also: ECMAScript Language Specification
Ensure that the keys are always in sorted order when the JSON is serialized to the database. In Python you would write:
json.dumps(obj, sort_keys=True)
With GSON this is harder to do, you may need to use a different library.

Converting JSON Object(s) from file using Java

I have a JSON file with no clue on how data will be in it nor the structure of data.
The only thing known is that it will have either an array of JSON objects or a single JSON object.
I need to get each object from the file and store it as a separate item. In case of array of objects in the file, I should get an array of JSON strings which I can store in DB.
Basically, I need to read this file and separate out each JSON object from it and store it in DB as a string.
One of the ways to do it was to use JACKSON ObjectMapper and assign these items to a Hashmap as key value pairs, but I am not sure though how it can be done If there are list of JSON Objects in the file.
Sample JSON File:
[
{
"name":"Bob",
"type":"Email",
"from":"a#a.com",
"to":"b#B.com",
"attachments":[...],
.
.
.
}
]
Do you know the Object structure that the JSON has(let it be Array or a single one) ? If Yes,
First load the json string form the file into an in memory string.
check the string for Array existence, by searching for '[',']' in the outer structure of multiple occurrences of '{' or '}'
once you know whether you have an array or a single object, you can pass it as object reference to either Jackson or GSON parsers
create in memory Array of JsonObject.class say List. It is actually better to enclose this List inside another class. say myJsonObjects and have a List inside it.
Let us see GSON parsers (by google), though Jackson can also be used in the similar implementation
Gson gson = new Gson();
if(isArray){
myJsonObjects jsonArray = gson.fromJson(jsonStringFromFile,myJsonObjects );
}
else{
gson.fromJson(jsonStringFromFile,JsonObject);
}
http://google-gson.googlecode.com/svn-history/trunk/gson/docs/javadocs/com/google/gson/Gson.html
Jackson is my favorite JSON-to-POJO library. It doesn't really matter where you're loading the JSON from (a URL or from the filesystem), there are handlers for several input sources.
Here's an example:
Map<String,Object> userData = mapper.readValue(new File("user.json"), Map.class);
As far as having an unknown number of JSON structures that you're about to parse, the first thing that comes to mind is to have a mapper for each type you're expecting. You could then wrap the parsing code in try/catch blocks so that if the first fails with whatever exception Jackson gives you when encountering an unexpected format, you can then try the next format and so on.
If you're just trying to generically parse JSON that you don't know the structure of beforehand, you can try something like this:
mapper.readValue(jsonString, new TypeReference<List<EntryType>>() {});
The documentation for Jackson is pretty good-- giving it a solid read-through should definitely help. Here's a good five minute tutorial: http://wiki.fasterxml.com/JacksonInFiveMinutes
I prefer use Gson:
Gson gson;
Map<String, Object>parameters=gson.fromJson(myString);
the rest is iterate the map, i hope help you

Categories

Resources