java中是否有一个函数可以检查json属性是否为null? [英] Is there a function in java to check if any json attribute is null?

查看:64
本文介绍了java中是否有一个函数可以检查json属性是否为null?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一条 JSON 格式的消息,已转换为 JSONObject ,并且我有大约30个必填字段,我必须检查这些字段是否为null或不是.如果这些必填字段之一为空,我将丢弃该消息,但是其他字段也可以为空而无需丢弃该消息.有没有什么有效的方法可以做到这一点,而无需遍历每个字段并使用 isNull()吗?

此外,JSON对象是嵌套的,因此简单的 anyNull()函数将不起作用,因为它仅在对象本身为null时返回,而在变量本身为null时才返回.>

我尝试使用gson将消息转换为POJO,并为10个对象创建了类

  Gson gson = new Gson();消息消息= gson.fromJson(msg,Message.class); 

但是由于使用简单的null检查器嵌套了许多类(其中一个是对象数组),因此无法正常工作.

解决方案

实际上,您的问题不是很清楚,因为您使用的是消息"一词,可以引用您的特定类,但也可以是更通用的引用已发送/已接收的消息.

类似于内存中的JSON元素:

  public static void failOnNullRecursively(final JsonElement jsonElement){如果(jsonElement.isJsonNull()){抛出新的IllegalArgumentException("null!");}如果(jsonElement.isJsonPrimitive()){返回;}如果(jsonElement.isJsonArray()){对于(最终JsonElement元素:jsonElement.getAsJsonArray()){failOnNullRecursively(element);}返回;}如果(jsonElement.isJsonObject()){for(final Map.Entry< String,JsonElement> e:jsonElement.getAsJsonObject().entrySet()){failOnNullRecursively(e.getValue());}返回;}抛出新的AssertionError(jsonElement);} 

流中的

或JSON文档:

 公共最终类FailOnNullJsonReader扩展JsonReader {私人FailOnNullJsonReader(最终阅读器阅读器){超级(阅读器);}公共静态JsonReader创建(最终阅读器阅读器){返回新的FailOnNullJsonReader(reader);}@Override公共无效nextNull(){抛出新的IllegalStateException(String.format(%@!为null,",getPath()));}} 

它们两者都将抛出 null .但似乎您也想验证 Message 实例:

如果这些必填字段之一为空,我将丢弃该消息,但是其他字段也可以为空而无需丢弃该消息.

因此,这说明了为什么上述空检查不符合您的需求.您正在寻找的是 JSR-303 .效率可能不如您所希望的那样高效(消息实例被反序列化,验证也需要时间和资源),但是从编码角度来看,效率可能很高:

 最终集< ConstraintViolation< V>>违规= validator.validate(message);如果(!violations.isEmpty()){抛出新的ConstraintViolationException(violations);} 

或什至将其直接集成到Gson中,以便为中间件提供服务:

 公共最终类PostReadTypeAdapterFactory< V>实现TypeAdapterFactory {私人最终谓词< ;?超级TypeToken<?>支持;私人最终BiConsumer< ;?超级TypeToken< V>,?超级VonRead;私人PostReadTypeAdapterFactory(最终Predicate< ;? Super TypeToken<?>支持,最终BiConsumer< ;? Super TypeToken< V>,?Super V> onRead){this.supports =支持;this.onRead = onRead;}公共静态< V>TypeAdapterFactory create(最终Predicate< Super TypeToken<>支持,最终BiConsumer< super TypeToken< V>,?super V> onRead){返回新的PostReadTypeAdapterFactory<>(支持,onRead);}@Override@Nullable公共< T>TypeAdapter< T>create(final Gson gson,final TypeToken< T> typeToken){如果(!supports.test(typeToken)){返回null;}最后的TypeAdapter< T>委托= gson.getDelegateAdapter(this,typeToken);返回新的TypeAdapter< T>(){@Override公共无效写入(最终JsonWriter输出,最终T值)引发IOException {proxy.write(输出,值);}@Override公共T读取(最终JsonReader中)引发IOException {最后的T readValue = proxy.read(in);@SuppressWarnings(未选中")最终V值=(V)readValue;@SuppressWarnings(未选中")最终的TypeToken< V>valueTypeToken =(TypeToken< V>)typeToken;onRead.accept(valueTypeToken,value);返回readValue;}};}} 

 公共最终课程Jsr303Support {私人Jsr303Support(){}公共静态< V>TypeAdapterFactory createTypeAdapterFactory(最终验证器验证器){返回PostReadTypeAdapterFactory.< V>创建(typeToken->typeToken.getRawType().isAnnotationPresent(Validate.class),(typeToken,值)->{最终Set< ConstraintViolation< V>违规= validator.validate(value);如果(!violations.isEmpty()){抛出新的ConstraintViolationException(violations);}});}} 

  @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface验证{} 

测试(为简便起见,使用龙目岛):

  @Validate@AllArgsConstructor@EqualsAndHashCode@ToString最后一课留言{@NotNullfinal String foo;@NotNull最后的字符串栏;@NotNull最后的String baz;} 

 公共最终类Jsr303SupportTest {私有静态最终验证器验证器;静止的 {尝试(最终ValidatorFactoryvalidatorFactory = Validation.buildDefaultValidatorFactory()){验证程序= validateatorFactory.getValidator();}}公共静态最终Gson gson =新的GsonBuilder().disableHtmlEscaping().disableInnerClassSerialization().registerTypeAdapterFactory(Jsr303Support.createTypeAdapterFactory(validator)).创造();@测试公共无效测试(){Assertions.assertEquals(新消息("1","2","3"),gson.fromJson("{\" foo \:\" 1,\"栏\:\" 2 \,\" baz \:\" 3 \}",Message.class));最终的ConstraintViolationException ex = Assertions.assertThrows(ConstraintViolationException.class,()-> gson.fromJson("{\" foo \:\" 1 \,\" bar \:null,\"baz \":\"3 \"},Message.class));Assertions.assertEquals(1,ex.getConstraintViolations().size());}} 

最后,可能是最高效的(就读取JSON流而言),但是与JSR-303相比非常有限(并且 NOT 在Gson中工作,因为Gson不会将空检查传播到下游(de)序列化器),可以用类似的功能性"替换 @NotNull 注释:

 公共最终类NotNullTypeAdapterFactory实现TypeAdapterFactory {//注意没有外部访问私人NotNullTypeAdapterFactory(){}@Override公共< T>TypeAdapter< T>create(final Gson gson,final TypeToken< T> typeToken){最后的TypeAdapter< T>委托= gson.getAdapter(typeToken);返回新的TypeAdapter< T>(){@Override公共无效写入(最终JsonWriter输出,@ Nullable最终T值)引发IOException {if(value == null){抛出新的IllegalArgumentException(typeToken +"with null");}proxy.write(输出,值);}@Override公共T读取(最终JsonReader中)引发IOException {@Nullable最终T值=委托.读取(输入);if(value == null){抛出新的IllegalArgumentException(typeToken +,在"+ in.getPath()处为null");}返回值;}};}} 

  @AllArgsConstructor@EqualsAndHashCode@ToString最后一课留言{@JsonAdapter(NotNullTypeAdapterFactory.class)final String foo;@JsonAdapter(NotNullTypeAdapterFactory.class)最后的字符串栏;@JsonAdapter(NotNullTypeAdapterFactory.class)最后的String baz;} 

 公共最终类NotNullTypeAdapterFactoryTest {公共静态最终Gson gson =新的GsonBuilder().disableHtmlEscaping().disableInnerClassSerialization().创造();@测试公共无效测试(){Assertions.assertEquals(新消息("1","2","3"),gson.fromJson("{\" foo \:\" 1,\"栏\:\" 2 \,\" baz \:\" 3 \}",Message.class));final IllegalArgumentException ex = Assertions.assertThrows(IllegalArgumentException.class,()-> gson.fromJson("{\" foo \:\" 1 \,\" bar \:null,\"baz \":\"3 \"},Message.class));Assertions.assertEquals(无论如何,以上内容仍然无法正常工作",例如ex.getMessage());}} 

第三个JSR-303看起来最适合您.

I have a message in JSON format that I converted to a JSONObject, and I have around 30 mandatory fields that I have to check for whether they're null or not. If one of these mandatory fields are null, I will discard the message, however other fields can be null without needing to discard the message. Is there any efficient way I can do this without going through each and every field and using isNull() ?

Also, the JSON objects are nested, so a simple anyNull() function would not work since it would only return if the object itself is null and not if the variables themselves are null.

I tried using gson to convert the message to a POJO, and created classes for 10 objects

Gson gson = new Gson();
Message message = gson.fromJson(msg, Message.class);

but since many classes are nested (and one of which is an array of objects) using simple null checkers don't work.

解决方案

Actually speaking your question is not very clear because you're using a word of "message" that refers your particular class, but can also be more generic referring sent/received messages.

So something like for JSON elements in memory:

public static void failOnNullRecursively(final JsonElement jsonElement) {
    if ( jsonElement.isJsonNull() ) {
        throw new IllegalArgumentException("null!");
    }
    if ( jsonElement.isJsonPrimitive() ) {
        return;
    }
    if ( jsonElement.isJsonArray() ) {
        for ( final JsonElement element : jsonElement.getAsJsonArray() ) {
            failOnNullRecursively(element);
        }
        return;
    }
    if ( jsonElement.isJsonObject() ) {
        for ( final Map.Entry<String, JsonElement> e : jsonElement.getAsJsonObject().entrySet() ) {
            failOnNullRecursively(e.getValue());
        }
        return;
    }
    throw new AssertionError(jsonElement);
}

or JSON documents in streams:

public final class FailOnNullJsonReader
        extends JsonReader {

    private FailOnNullJsonReader(final Reader reader) {
        super(reader);
    }

    public static JsonReader create(final Reader reader) {
        return new FailOnNullJsonReader(reader);
    }

    @Override
    public void nextNull() {
        throw new IllegalStateException(String.format("null at %@!", getPath()));
    }

}

Both of them will throw on null. But it also seems that you want to validate Message instances:

If one of these mandatory fields are null, I will discard the message, however other fields can be null without needing to discard the message.

So this tells why the above null-checks won't fit your needs. What you're looking for is JSR-303. It won't be that efficient as you might want to want it to be (message instances are deserialized, validation takes time and resources too), but it might be efficient from the coding perspective:

final Set<ConstraintViolation<V>> violations = validator.validate(message);
if ( !violations.isEmpty() ) {
    throw new ConstraintViolationException(violations);
}

or even integrate it right into Gson so that it serves middleware:

public final class PostReadTypeAdapterFactory<V>
        implements TypeAdapterFactory {

    private final Predicate<? super TypeToken<?>> supports;
    private final BiConsumer<? super TypeToken<V>, ? super V> onRead;

    private PostReadTypeAdapterFactory(final Predicate<? super TypeToken<?>> supports, final BiConsumer<? super TypeToken<V>, ? super V> onRead) {
        this.supports = supports;
        this.onRead = onRead;
    }

    public static <V> TypeAdapterFactory create(final Predicate<? super TypeToken<?>> supports, final BiConsumer<? super TypeToken<V>, ? super V> onRead) {
        return new PostReadTypeAdapterFactory<>(supports, onRead);
    }

    @Override
    @Nullable
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        if ( !supports.test(typeToken) ) {
            return null;
        }
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter out, final T value)
                    throws IOException {
                delegate.write(out, value);
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                final T readValue = delegate.read(in);
                @SuppressWarnings("unchecked")
                final V value = (V) readValue;
                @SuppressWarnings("unchecked")
                final TypeToken<V> valueTypeToken = (TypeToken<V>) typeToken;
                onRead.accept(valueTypeToken, value);
                return readValue;
            }
        };
    }

}

public final class Jsr303Support {

    private Jsr303Support() {
    }

    public static <V> TypeAdapterFactory createTypeAdapterFactory(final Validator validator) {
        return PostReadTypeAdapterFactory.<V>create(
                typeToken -> typeToken.getRawType().isAnnotationPresent(Validate.class),
                (typeToken, value) -> {
                    final Set<ConstraintViolation<V>> violations = validator.validate(value);
                    if ( !violations.isEmpty() ) {
                        throw new ConstraintViolationException(violations);
                    }
                }
        );
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Validate {
}

And the test (using Lombok for brevity):

@Validate
@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Message {

    @NotNull
    final String foo;

    @NotNull
    final String bar;

    @NotNull
    final String baz;

}

public final class Jsr303SupportTest {

    private static final Validator validator;

    static {
        try ( final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory() ) {
            validator = validatorFactory.getValidator();
        }
    }

    public static final Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .disableInnerClassSerialization()
            .registerTypeAdapterFactory(Jsr303Support.createTypeAdapterFactory(validator))
            .create();

    @Test
    public void test() {
        Assertions.assertEquals(new Message("1", "2", "3"), gson.fromJson("{\"foo\":\"1\",\"bar\":\"2\",\"baz\":\"3\"}", Message.class));
        final ConstraintViolationException ex = Assertions.assertThrows(ConstraintViolationException.class, () -> gson.fromJson("{\"foo\":\"1\",\"bar\":null,\"baz\":\"3\"}", Message.class));
        Assertions.assertEquals(1, ex.getConstraintViolations().size());
    }

}

And finally, probably the most efficient (in terms of reading JSON stream), but very limited whencompared to JSR-303 (and NOT working in Gson because Gson does not propagate null-checking to downstream (de)serializers), way that could replace @NotNull with a similar "functional" annotation:

public final class NotNullTypeAdapterFactory
        implements TypeAdapterFactory {

    // note no external access
    private NotNullTypeAdapterFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        final TypeAdapter<T> delegate = gson.getAdapter(typeToken);
        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter out, @Nullable final T value)
                    throws IOException {
                if ( value == null ) {
                    throw new IllegalArgumentException(typeToken + " with null");
                }
                delegate.write(out, value);
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                @Nullable
                final T value = delegate.read(in);
                if ( value == null ) {
                    throw new IllegalArgumentException(typeToken + " with null at " + in.getPath());
                }
                return value;
            }
        };
    }

}

@AllArgsConstructor
@EqualsAndHashCode
@ToString
final class Message {

    @JsonAdapter(NotNullTypeAdapterFactory.class)
    final String foo;

    @JsonAdapter(NotNullTypeAdapterFactory.class)
    final String bar;

    @JsonAdapter(NotNullTypeAdapterFactory.class)
    final String baz;

}

public final class NotNullTypeAdapterFactoryTest {

    public static final Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .disableInnerClassSerialization()
            .create();

    @Test
    public void test() {
        Assertions.assertEquals(new Message("1", "2", "3"), gson.fromJson("{\"foo\":\"1\",\"bar\":\"2\",\"baz\":\"3\"}", Message.class));
        final IllegalArgumentException ex = Assertions.assertThrows(IllegalArgumentException.class, () -> gson.fromJson("{\"foo\":\"1\",\"bar\":null,\"baz\":\"3\"}", Message.class));
        Assertions.assertEquals("whatever here, the above does not work anyway", ex.getMessage());
    }

}

The third, JSR-303, looks like the best for you.

这篇关于java中是否有一个函数可以检查json属性是否为null?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆