How to convert string Map to JSON string in java? - json

I have fact an issue related to convert string map to string json with below example
public class Demo {
public static void main(String[] args) throws JsonProcessingException {
String stringRequest = "{A=12, B=23}";
System.out.println(new Gson().toJson(stringRequest));
}
}
```
OUTPUT: "{A\u003d12, B\u003d23}"
Please you help me how can I map this to json string.

It is a bit unclear what actually is your problem but the main thing in your example is that you are serializing a String object to JSON. That is why you get such an output, it is not a presentation of a Map but a String.
However with that string you can easily create a Map which you can then serialize. Not saying there is any point on that unless you want to do some cleaning or so but anyway:
// Check first which kind of types are keys & values
// keys are always Strings and here it seems that values can be Integers
Type type = new TypeToken<Map<String, Integer>>(){}.getType();
// Create the actual map from that string
Map<String, Integer> map = getGson().fromJson(stringRequest, type);
// Serialize the map to the console (added pretty printing here)
System.out.println(new Gson().toJson(map));
and you can see:
{ "A": 12, "B": 23 }

Related

jackson reading in non-existent and null values to “” and marshalling out “” to non-existent values?

I read through this post Jackson: deserializing null Strings as empty Strings which has this cool trick
mapper.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
THEN on the flipside, I read through this post Jackson serialization: ignore empty values (or null) which has this cool trick
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
This is VERY VERY close except I really don't want incoming data to be null in any case. I have the following code printing 4 situations with the above settings BUT want to fix the null piece so any json we unmarshal into java results in
public class MapperTest {
private static final Logger log = LoggerFactory.getLogger(MapperTest.class);
private ObjectMapper mapper = new ObjectMapper();
public MapperTest() {
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
}
public static void main(String[] args) throws JsonProcessingException {
new MapperTest().start();
}
private void start() throws JsonProcessingException {
//write out java color=null resulting in NO field...
String val = mapper.writeValueAsString(new Something());
log.info("val="+val);
Something something = mapper.readValue(val, Something.class);
log.info("value='"+something.getColor()+"'");
//write out java color="" resulting in NO field...
Something s = new Something();
s.setColor("");
String val2 = mapper.writeValueAsString(new Something());
log.info("val="+val2);
String temp = "{\"color\":null,\"something\":0}";
Something something2 = mapper.readValue(temp, Something.class);
log.info("value2='"+something2.getColor()+"'");
}
}
The output is then
INFO: val={"something":0}
INFO: value='null'
INFO: val={"something":0}
INFO: value2=''
NOTE: The value = 'null' is NOT what I desire and want that to also be empty string. Notice that if customers give a color:'null', it does result in empty string. Non-existence should result in the same thing for us "".
This is a HUGE win in less mistakes in this area 'for us' I mean.
thanks,
Dean

How to extract the a value from a JSON object by matching the key in a Map using Java 8?

I'm using Java 8 and jackson to try and get an int value from a json object. Here is some similar code I used to verify the structure.
HashMap<String, Object> myMap = new HashMap<String, Object>();
myMap
.entrySet()
.stream()
.forEach(x -> System.out.println(x.getKey() + " => " + x.getValue()));
and the result:
myKey1 => {"number":1}
myKey2 => {"number":1}
myKey3 => {"number":2}
What I'm trying to do is use the key, like myKey1 to find the json value i.e. {"number":1} and pull out the actual number, which for that key is 1.
However, I don't all know the values of the key's, just the key I want to match up. So I have to do this dynamically. I know the structure of the values and that is always the same, except the number can be different.
I think because I don't know the keys and their order, plus I'm using entrySet, that is forcing me into using Optional, which is making this more difficult.
Here is my code where I'm actually using the key to pull the json value:
Optional<Object> object = myMap.entrySet().stream()
.filter(e -> e.getKey().equals(myKey))
.map(Map.Entry::getValue)
.findFirst();
However, the stream pulls back
Optional[{"number":1}]
and I can't seem to get the number out so I can return it from a method. I don't actually
have a Java class for the object, so I assume that is why it's returning Optional as I was getting a compile error without it, but I'm not sure.
Any ideas as to the right way to do this?
Why iterating over all the entries of the map and do a linear search by key, to get the value of the entry?
Just get the value directly:
Object value = myMap.get(myKey);
Now, with regard to the number inside value, as you say you don't have a class that represents the values of the map, the most likely thing is that the JSON library you're using is creating a Map for each value. So from now on, let's assume that the values are actually some implementation of Map:
Integer = null;
if (value != null) {
// What's the type of the value? Maybe a Map?
if (value instanceof Map) {
Map<String, Object> valueAsMap = (Map<String, Object>) value;
number = (Integer) valueAsMap.get("number");
}
}
I'm assuming the numbers are Integers, but they can perfectly be Long instances or even Doubles or BigDecimals. Just be sure of the exact type, so the second cast doesn't fail.
EDIT: For completeness, here's the code for when the values are not maps, but of some class that represents a json node. Here I'm using JsonNode from Jackson library, but the approach is very similar for other libs:
Integer = null;
if (value != null) {
if (value instanceof JsonNode) {
JsonNode valueAsNode = (JsonNode) value;
number = (Integer) valueAsNode.get("number").numberValue();
}
}
findFirst() returns an Optional. You need to call .get() or .orElse() or .orElseThrow() after you call findFirst().
You can then cast the Object to JsonNode and retrieve the value. Here is the full code:-
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
HashMap<String, Object> myMap = new HashMap<String, Object>();
myMap.put("myKey1", mapper.readTree("{\"number\":1}"));
myMap.put("myKey2", mapper.readTree("{\"number\":1}"));
myMap.put("myKey3", mapper.readTree("{\"number\":2}"));
System.out.println(getNumberUsingMapKey(myMap, "myKey3"));
}
private static int getNumberUsingMapKey(Map<String, Object> map, String key) throws Exception {
return Optional.of(map.get(key))
.map(o -> ((JsonNode) o).get("number").asInt())
.get(); //or use one of the following depending on your needs
// .orElse(-1);
// .orElseThrow(Exception::new);
}
//or use this method
private static int getNumberUsingMapKeyWithoutOptional(Map<String, Object> map, String key) throws Exception {
Object o = map.get(key);
return ((JsonNode) o).get("number").asInt();
}
Output
2
Unfortunately I didn't describe my problem statement well from the start, but this is the final working code that I was able to piece from the other answers.
This is what the data structure looked like. A key that had a value which was a JSON object that had a key and an int, in other words a nested JSON object.
Key: myKey1 Value:{"number":1}
Key: myKey2 Value:{"number":1}
Key: myKey3 Value:{"number":2}
I'm posting it in case some else runs into this use case. This may not be the best way to do it, but it works.
Object value = myMap.get(keyName);
ObjectMapper mapper = new ObjectMapper();
String jsonString = (String) value;
JsonNode rootNode = mapper.readTree(jsonString);
JsonNode numberNode = rootNode.path("number");
System.out.println("number: " + numberNode.intValue());
and the result:
number: 1

Handle JSON which sends array of items but sometimes empty string in case of 0 elements

I have a JSON which sends array of element in normal cases but sends empty string "" tag without array [] brackets in case of 0 elements.
How to handle this with Gson? I want to ignore the error and not cause JSONParsingException.
eg.
"types": [
"Environment",
"Management",
"Computers"
],
sometimes it returns:
"types" : ""
Getting the following exception: Expected BEGIN ARRAY but was string
Since you don't have control over the input JSON string, you can test the content and decide what to do with it.
Here is an example of a working Java class:
import com.google.gson.Gson;
import java.util.ArrayList;
public class Test {
class Types {
Object types;
}
public void test(String input) {
Gson gson = new Gson();
Types types = gson.fromJson(input,Types.class);
if(types.types instanceof ArrayList) {
System.out.println("types is an ArrayList");
} else if (types.types instanceof String) {
System.out.println("types is an empty String");
}
}
public static void main(String[] args) {
String input = "{\"types\": [\n" +
" \"Environment\",\n" +
" \"Management\",\n" +
" \"Computers\"\n" +
" ]}";
String input2 = "{\"types\" : \"\"}";
Test testing = new Test();
testing.test(input2); //change input2 to input
}
}
If a bad JSON schema is not under your control, you can implement a specific type adapter that would try to determine whether the given JSON document is fine for you and, if possible, make some transformations. I would recomment to use #JsonAdapter in order to specify improperly designed types (at least I hope the entire API is not improperly designed).
For example,
final class Wrapper {
#JsonAdapter(LenientListTypeAdapterFactory.class)
final List<String> types = null;
}
where LenientListTypeAdapterFactory can be implemented as follows:
final class LenientListTypeAdapterFactory
implements TypeAdapterFactory {
// Gson can instantiate it itself, let it just do it
private LenientListTypeAdapterFactory() {
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Obtaining the original list type adapter
#SuppressWarnings("unchecked")
final TypeAdapter<List<?>> realListTypeAdapter = (TypeAdapter<List<?>>) gson.getAdapter(typeToken);
// And wrap it up in the lenient JSON type adapter
#SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new LenientListTypeAdapter(realListTypeAdapter);
return castTypeAdapter;
}
private static final class LenientListTypeAdapter
extends TypeAdapter<List<?>> {
private final TypeAdapter<List<?>> realListTypeAdapter;
private LenientListTypeAdapter(final TypeAdapter<List<?>> realListTypeAdapter) {
this.realListTypeAdapter = realListTypeAdapter;
}
#Override
public void write(final JsonWriter out, final List<?> value)
throws IOException {
realListTypeAdapter.write(out, value);
}
#Override
public List<?> read(final JsonReader in)
throws IOException {
// Check the next (effectively current) JSON token
switch ( in.peek() ) {
// If it's either `[...` or `null` -- we're supposing it's a "normal" list
case BEGIN_ARRAY:
case NULL:
return realListTypeAdapter.read(in);
// Is it a string?
case STRING:
// Skip the value entirely
in.skipValue();
// And return a new array list.
// Note that you might return emptyList() but Gson uses mutable lists so we do either
return new ArrayList<>();
// Not anything known else?
case END_ARRAY:
case BEGIN_OBJECT:
case END_OBJECT:
case NAME:
case NUMBER:
case BOOLEAN:
case END_DOCUMENT:
// Something definitely unexpected
throw new MalformedJsonException("Cannot parse " + in);
default:
// This would never happen unless Gson adds a new type token
throw new AssertionError();
}
}
}
}
Here is it how it can be tested:
for ( final String name : ImmutableList.of("3-elements.json", "0-elements.json") ) {
try ( final Reader reader = getPackageResourceReader(Q43562427.class, name) ) {
final Wrapper wrapper = gson.fromJson(reader, Wrapper.class);
System.out.println(wrapper.types);
}
}
Output:
[Environment, Management, Computers]
[]
If the entire API uses "" for empty arrays, then you can drop the #JsonAdapter annotation and register the LenientListTypeAdapterFactory via GsonBuilder, but add the following lines to the create method in order not to break other type adapters:
if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) {
// This tells Gson to try to pick up the next best-match type adapter
return null;
}
...
There are a lot of weirdly designed JSON response choices, but this one hits the top #1 issue where nulls or empties are represented with "". Good luck!
Thanks for all your answers.
The recommed way as mentioned in above answers would be to use TypeAdapters and ExclusionStrategy for GSON.
Here is a good example Custom GSON desrialization

Bad saving in Unity 3D with JSON

My source code of voxel terrain can't save the data. I have a 3D char array called terrain and when I save, then it's result is empty json. The result is:
{}
The source code is:
public void TerrainSave() {
LoadingSavingClass myObject = new LoadingSavingClass();
myObject.terrain = terrain;
string json = JsonUtility.ToJson(myObject);
File.WriteAllText(Application.streamingAssetsPath + "/terrain/save.ter", json);
if(json == "{}")
{
Debug.Log("Saved clear data");
}
}
The class is:
[Serializable]
public class LoadingSavingClass
{
public char[,,] terrain = new char[128, 32, 128];
}
The saved 3D char char array isn't empty, I put some data into it before saving.
As mentioned here in JSON Serialization Docs JsonUtility.ToJson will only serialize the same types as you can serialize in Unity's inspector. char[,,,] won't serialize in Unity's inspector so it won't serialize with JsonUtility.ToJson

Can I override Gson's built-in number converters directly (not by delegation)?

I am using Gson to convert my JSON data to a Map<String, Object>. My JSON has some integer (actually long) fields, and I want them to be parsed as long (obviously). However, Gson parses them as doubles. How can I make Gson parse them as longs/integers? I've seen How to deserialize a list using GSON or another JSON library in Java? but I don't want to create a strongly-typed custom class, I'll be using a Map. I've also seen Android: Gson deserializes Integer as Double and a few other questions which I've thought might be duplicates, but all the answers either point to creating a strongly-typed class, creating extra functions that play role in deserialization or using completely another library.
Isn't there a simple way in Google's own JSON serializer/deserializer that will simply deserialize an integer (yeah, a number without a dot at all) as an integer and not double, as it should have been as default in the first place? If I wanted to send a floating point, I'd be sending 2.0, not 2 from my server JSON. Why on Earth am I getting a double and how do I get rid of it?
UPDATE: Even though I've clearly explained, some people still don't understand the simple fact that I am not in for another library (e.g. Jackson) and I'm aware of the simple fact that any parser should be able to identify 2.0 as a floating-point and 2 as a pure integer and parse accordingly, so please stop pointing me to telling why it's that way because it's simply incorrect and is not an excuse not to parse integers correctly. So, no, this is not a duplicate of Gson. Deserialize integers as integers and not as doubles.
You can't.
Long answer
You can't override gson's built-in numbers converters.
I've made a short code test to peek under the hood which types gson tries to find a delegated converter.
package net.sargue.gson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.intellij.lang.annotations.Language;
import java.io.IOException;
import java.util.Map;
public class SO36528727 {
public static void main(String[] args) {
#Language("JSON")
String json = "{\n" +
" \"anInteger\": 2,\n" +
" \"aDouble\": 2.0\n" +
"}";
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new LongDeserializerFactory())
.create();
Map<String, Object> m =
gson.fromJson(json,
new TypeToken<Map<String, Object>>() {}.getType());
System.out.println(m.get("aDouble").getClass());
System.out.println(m.get("anInteger").getClass());
}
private static class LongDeserializerFactory
implements TypeAdapterFactory
{
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
System.out.println("type = " + type);
if (type.getRawType().equals(String.class)) {
TypeAdapter<String> stringAdapter =
gson.getDelegateAdapter(this, TypeToken.get(String.class));
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) throws IOException {
stringAdapter.write(out, (String) value);
}
#SuppressWarnings("unchecked")
#Override
public T read(JsonReader in) throws IOException {
String s = stringAdapter.read(in);
System.out.println("s = " + s);
return (T) s;
}
};
} else
return null;
}
}
}
The execution result is this:
type = java.util.Map<java.lang.String, java.lang.Object>
type = java.lang.String
s = anInteger
s = aDouble
class java.lang.Double
class java.lang.Double
So, you can see that gson looks just for two converters: the whole Map<> thing and the basic String. But no Double or Integer or Number or even Object. So you CAN'T override it unless you override it from a higher place like when dealing with a Map. And that was answered on the thread you reference on the question.