How to throw exception while converting from JsonStr to PojoClassObj if any key/value is missing in the JsonStr?

僤鯓⒐⒋嵵緔 提交于 2020-07-07 13:27:28

问题


I tried using @NotNull annotation but it did not work. If any field is missing in the JsonString, it gives null(String) or zero(int). But what I want is if any field is missing in the JsonStr defined in the class should throw an exception.

Add: My PojoClass may have object reference or multilevel object reference. I am using Gson for the conversion of the String to obj. for more clarification, I have added my code below:

JsonStr:

{
   "name":"John",
   "id":1,
   "roll":100,
   "c":{
      "city":"Dhaka",
      "school":"B. govt. School"
   }
}

Code:

public class C {

    private String city;
    private String school;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

}

ClassB:

public class B {

    private String name;
    private int id;
    @NotNull
    private int roll;
    private C c;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getRoll() {
        return roll;
    }

    public void setRoll(int roll) {
        this.roll = roll;
    }

    public C getC() {
        return c;
    }

    public void setC(C c) {
        this.c = c;
    }

}

MainClass:

try {
                B obj = new B();
                String str = "{\"name\":\"John\",\"id\":1,\"c\":{\"city\":\"dhaka\",\"school\":\"school\"}}";
                obj = gson.fromJson(str, B.class);

            } catch (RuntimeException e) {
                 System.out.println("exception Message");

            }

For the field roll, I used @NotNull to throw an exception if that field is not present in the JsonStr but it gives 0 value without throwing any exception.

How can I implement that?

Please don't say this is duplicate, because I have seen these questions:

  • GSON throwing null pointer exception when a field is missing in json [duplicate]

  • What is a NullPointerException, and how do I fix it?


回答1:


@NotNull is not a part of Gson, and it cannot handle it by default (unless you add support yourself). Also, nullability-check annotations should not be applied to primitives fields -- generally speaking it makes not much sense. Also, the default int fields values are 0, and Gson does not check if a particular field was read from a JSON document. However, you can easily implement it if you:

  • slightly change your mapping by changing the primitive int to its nullable wrapper Integer;
  • implement a custom post-processing validation type adapter.

For example,

final class Person {

    final String name = null;

    final int id = Integer.valueOf(0); // final primitive fields are inlined by javac

    @NotNull
    final Integer roll = null;

    @SerializedName("c")
    final Location location = null;

}
final class Location {

    final String city = null;

    final String school = null;

}

The next step is just creating your own type adapter to check the nullable fields.

final class NotNullTypeAdapterFactory
        implements TypeAdapterFactory {

    // The type adapter factory holds no state, so no need to instantiate it multiple times
    private static final TypeAdapterFactory notNullTypeAdapterFactory = new NotNullTypeAdapterFactory();

    private NotNullTypeAdapterFactory() {
    }

    static TypeAdapterFactory getNotNullTypeAdapterFactory() {
        return notNullTypeAdapterFactory;
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        final Collection<Field> notNullFields = getNotNullFields(typeToken.getRawType());
        // If no @NotNull fields found, then just tell Gson to pick the next best type adapter
        if ( notNullFields.isEmpty() ) {
            return null;
        }
        // If there's at least one @NotNull field, get the original type adapter
        final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter out, final T value)
                    throws IOException {
                delegateTypeAdapter.write(out, value);
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                try {
                    // Read the value ...
                    final T value = delegateTypeAdapter.read(in);
                    // ... and make some post-processing
                    for ( final Field f : notNullFields ) {
                        if ( f.get(value) == null ) {
                            throw new MalformedJsonException(f + " has no value");
                        }
                    }
                    return value;
                } catch ( final IllegalAccessException ex ) {
                    throw new IOException(ex);
                }
            }
        };
    }

    private static Collection<Field> getNotNullFields(final Class<?> clazz) {
        // Primitive types and java.lang.Object do not have @NotNull
        if ( clazz.isPrimitive() || clazz == Object.class ) {
            return emptyList();
        }
        // Scan the whole hierarchy from the bottom subclass to the top superclass (except java.lang.Object we mentioned above)
        final Collection<Field> notNullFields = new ArrayList<>();
        for ( Class<?> c = clazz; c != Object.class; c = c.getSuperclass() ) {
            for ( final Field f : c.getDeclaredFields() ) {
                if ( f.isAnnotationPresent(NotNull.class) ) {
                    // Don't forget to make private fields accessible
                    f.setAccessible(true);
                    notNullFields.add(f);
                }
            }
        }
        return notNullFields;
    }

}

Test (uses some Google Guava and custom resource readers):

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(getNotNullTypeAdapterFactory())
        .create();

public static void main(final String... args)
        throws IOException {
    for ( final String resourceName : ImmutableList.of("file-with-roll.json", "file-without-roll.json") ) {
        System.out.println("Deserializing " + resourceName);
        try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q44362030.class, resourceName) ) {
            try {
                final Person person = gson.fromJson(jsonReader, Person.class);
                System.out.println(person.name + " " + person.roll);
            } catch ( final Exception ex ) {
                System.out.println("FAILED! " + ex.getMessage());
            }
        }
    }
}

Output:

Deserializing file-with-roll.json
John 100
Deserializing file-without-roll.json
FAILED! com.google.gson.stream.MalformedJsonException: final java.lang.Integer q44362030.Person.roll has no value

Similarly, you can create your another TypeAdapter to check all fields automatically not even needing @NotNull, but this needs a more complicated implementation.




回答2:


Have you try using Wrapper class instead primitive?

use this:

@NotNull private Integer roll;



来源:https://stackoverflow.com/questions/44362030/how-to-throw-exception-while-converting-from-jsonstr-to-pojoclassobj-if-any-key

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!