4

Using gson, I use this cumbersome approach to make sure a required property has a desired value:

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class ScratchSpace {

    public static void main(String[] args) {

        // create json object from source data - in my real code, this is sourced externally
        JsonObject json = new JsonParser().parse("{  \"product\": \"foobar\"}").getAsJsonObject();

        // does this object have a key called product, which is a string, and equal to our expected value?
        boolean correctProduct = false;
        if (json.has("product")) {
            JsonElement productElement = json.get("product");
            if (productElement.isJsonPrimitive()) {
                String product = productElement.getAsString();
                if ("foobar".equals(product)) {
                    correctProduct = true;
                }

            }
        }
        System.out.println("correctProduct = " + correctProduct);
    }

}

I'm almost certain I'm doing this suboptimally. Is there a simple, readable, short-ish one-liner to achieve the same?

Edit: if possible, I'd like to keep using gson.

Steve McLeod
  • 51,737
  • 47
  • 128
  • 184

3 Answers3

1

Using java.util.Optional, the following works:

final boolean correctProduct = Optional.ofNullable(json.get("product"))
            .filter(JsonPrimitive.class::isInstance)
            .map(JsonPrimitive.class::cast)
            .map(JsonPrimitive::getAsString)
            .filter("foobar"::equals)
            .isPresent();
Smutje
  • 17,733
  • 4
  • 24
  • 41
  • Using streams is an interesting approach; I was expecting gson to have some convenience method for doing this too. – Steve McLeod Jan 31 '17 at 09:07
  • Actually, `javax.json.JsonObject` has a `getString` method with possibility of a default value if the value is not a `String`, but appearently, Google rather created their own data model... ;-) PS.: I for one would welcome if more developers would move away from the "return null in case of error" to a "return an Optional" scheme. – Smutje Jan 31 '17 at 09:09
  • 1
    @Smutje AFAIR, JSON Processing was introduced to Java 7 even prior to Java 8 with its optionals, whilst Gson was initially released in 2008, almost ten years ago, so I think the javax.json authors just made a rethink of existing libraries like Gson or Jackson. Returning a `null` is cheaper, but yes it's not that convenient for such cases if one prefers oneliners... – Lyubomyr Shaydariv Jan 31 '17 at 09:23
  • OK, this would explain the difference in JSON representation in Java. – Smutje Jan 31 '17 at 09:25
0

You can write a custom deserializer like this, register it, and use fromJson method to obtain object directly from json string. In this way, you can return null or throw exception in deserializer if the json string is not in expected format.

Note that you don't have to set each field seperately. After performing custom checks, you can use default deserialization from context.

EDIT: If you want to obtain just true/false instead of the complete object, then you can write a MyBoolean class, holding a simple boolean value and use fromJson method to deserialize to MyBoolean class. The custom deserializer will perform only the desired checks and set the content of MyBoolean instance appropriately. Furthermore, (I guess) if you extend this MyBoolean from Boolean, you can use it as boolean too.

EDIT 2: I didn't have to time to include a sample code before. Here is what I suggest:

package io.ram.ram;

import java.lang.reflect.Type;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

public class Tester {

    public static class MyBoolean {
        private boolean value;

        public void set(boolean value) {
            this.value = value;
        }

        public boolean get() {
            return value;
        }
    }

    public static class MyAdapter implements JsonDeserializer<MyBoolean> {

        public MyBoolean deserialize(JsonElement json, Type type, JsonDeserializationContext context)
                throws JsonParseException {
            MyBoolean result = new MyBoolean();
            result.set(false);

            try {
                result.set(json.getAsJsonObject().get("product").getAsString().equals("foobar"));
            } catch (Exception e) {

            }
            return result;
        }

    }

    public static void main(String[] args) {
        Gson gson = new GsonBuilder().registerTypeAdapter(MyBoolean.class, new MyAdapter()).create();

        System.out.println(gson.fromJson("{\"product\": \"foobar\"}", MyBoolean.class).get());
        System.out.println(gson.fromJson("{\"product\": \"foobaz\"}", MyBoolean.class).get());
    }

}

As I said, after you register a type adapter for custom serialization, you can achieve what you want with a single line. (Note: Boolean class is final, so we cannot extend it. Sorry for this wrong information.)

You can parametrize MyAdapter for strings "product" and "foobar" of course, thus you don't have to create such classes for every possible cases.

Community
  • 1
  • 1
ram
  • 1,099
  • 1
  • 7
  • 22
-1

I know you said GSON, but the pojo based approach jackson offers makes what you want to do just too convenient to not to post:

Simple pojo:

public class FooPojo {
    @JsonProperty
    private String product;

    public String getProduct() {
        return product;
    }

    public void setProduct(String product) {
        this.product = product;
    }

    public boolean isProductEqualTo(String check) {
        return product.equals(check);
    }
}

Parse and check:

public static void main(String[] args) throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    FooPojo fooPojo = objectMapper.readValue("{  \"product\": \"foobar\"}", FooPojo.class);
    System.out.println(fooPojo.isProductEqualTo("foobar"));
}
baao
  • 71,625
  • 17
  • 143
  • 203