Flink反序列化之JsonDeserializationSchema和JSONKeyValueDeserializationSchema

  • Post author:
  • Post category:其他




1、引言

Flink Kafka的使用者需要知道如何将Kafka中的二进制数据转换为Java / Scala对象,大多数的程序员都会使用new SimpleStringSchema()来反序列化Kafka中的数据,然后使用alibaba提供的fastJson来解析数据,虽然这样可以实现业务的需求,但是存在很多不确定的因素。比如由于业务的原因,接受的json数据字段不稳定,有的数据多一个字段,有的业务少一个字段,这种情况的空间使用不好。再比如,有的字段值为null,我们需要对值进行判断是否为null,否则容易出现空指针异常。对于json字段少的数据这种情况造成的影响不大,但是对于多字段的json数据呢,造成的效率减低是无法忍受的,所以,博主今天在这里给大家分享两种flink两种反序列化机制

JsonDeserializationSchema



JSONKeyValueDeserializationSchema



2、JsonDeserializationSchema和JSONKeyValueDeserializationSchema简介

JsonDeserializationSchema(和JSONKeyValueDeserializationSchema)将序列化的JSON转换为ObjectNode对象,可以使用从中访问字段objectNode.get(“field”).as(Int/String/…)。KeyValue objectNode包含一个“键”和“值”字段,其中包含所有字段,以及一个可选的“元数据”字段,用于显示此消息的偏移量/分区/主题。



废话不多说,上demo
	    //配置Kafka的相关信息
        Properties properties = new Properties();
        properties.setProperty("bootstrap.servers", "ip:port");
        properties.setProperty("group.id", "g1");
        //读取Kafka的数据源  订阅主题
        DataStreamSource<ObjectNode> streamSource = env.addSource(new FlinkKafkaConsumer011<>("topic", new JSONKeyValueDeserializationSchema(true), properties));
		
		streamSource.process(new ProcessFunction<ObjectNode, String>() {
            @Override
            public void processElement(ObjectNode value, Context ctx, Collector<String> out) throws Exception {
                JsonNode name = value.get("value").get("name");
               System.out.println(name);
        });
        env.execute("test-version02");     

以上是对简单json的数据处理,通过拿json的字段获取字段的值,那么小伙伴们的问题就了,同fastJson使用一样,此解析方式会由于字段本身的存在与否或者字段值的原因导致程序报错么?接下来我们验证以下。

准备测试数据

{"phone_num":"12345678","name":"abcd","age":"18"}

奉上代码

streamSource.process(new ProcessFunction<ObjectNode, String>() {
            @Override
            public void processElement(ObjectNode value, Context ctx, Collector<String> out)   {
                JsonNode phone_num = value.get("value").get("phone_num");
                System.out.println("phone_num"+phone_num);
                JsonNode name = value.get("value").get("name");
                System.out.println("name"+name);
                JsonNode age = value.get("value").get("age");
                System.out.println("age"+age);
                JsonNode sex = value.get("value").get("sex");
                System.out.println("sex"+sex);
            }
        });
        env.execute("test-version02");

我们测试的phone_num,name是正常的字段,但是age的字段为null,sex是不存在的字段。

结果如下

phone_num"12345678"
name"abcd"
agenull
sexnull

很显然,程序并没有出现异常,正常的字段按数据显示字段的值,值为null的age字段,也没有出现空指针异常,不存在的字段sex,被赋予了默认值null。

相信看到这里,我们伟大的工程师们又有问题了,对于基本的json数据没问题,但是如果是复杂的数据呢?比如字段的值又是一个json,或者字段的值是一个json数组怎么办?没关系,博主帮你们把这些问题都准备好了,往下看。

准备测试数据

{"phone_num":[{"aa":"11","bb":"22"},{"cc":"33","dd":"44"}],"name":"abcd"}

奉上测试代码

streamSource.process(new ProcessFunction<ObjectNode, String>() {
            @Override
            public void processElement(ObjectNode value, Context ctx, Collector<String> out) throws Exception {
                JsonNode json1 = value.get("value").get("phone_num");
                for(Iterator<JsonNode> elements = json1.elements();elements.hasNext();){
                    JsonNode next = elements.next();
                    System.out.println(next);
                    for(Iterator<Map.Entry<String, JsonNode>> next0 = next.fields();next0.hasNext();){
                        Map.Entry<String, JsonNode> entry = next0.next();
                        System.out.println(entry.getKey());
                        System.out.println(entry.getValue());
                    }
                }
            }
        });

因为只有phone_num字段值是数组,所以我们值关心这个字段,同JsonNode的elements()方法我们可以获取到数组里面的每一个json,再将获取到的每一个json,通过fields()方法获取json数据里面的每一个字段和字段值,看一下我们呢刚才运行的结果。

{"aa":"11","bb":"22"}
aa
"11"
bb
"22"
{"cc":"33","dd":"44"}
cc
"33"
dd
"44"

如果你认为这两种反序列化仅仅到这里,那么你就大错特错了,关于其他的API,伙伴们可以自己查看源码(官方文档没有解释)或者使用开发工具查看。

接下来再给大家分享一下,其实通过这种反序列化,我们还可以直接获取flink自身的数据类型,要知道,对于流式计算而言,自身提供的数据类型是由于JDK数据类型的,话不多说,上代码。

   streamSource.process(new ProcessFunction<ObjectNode, String>() {
            @Override
            public void processElement(ObjectNode value, Context ctx, Collector<String> out) throws Exception {
                JsonNode phone_num = value.get("value").get("phone_num");
                phone_num.longValue();
                phone_num.binaryValue();
                phone_num.floatValue();
			}
	});

惊不惊喜,意不意外,伙伴们,其实我们还可以这样

streamSource.process(new ProcessFunction<ObjectNode, String>() {
            @Override
            public void processElement(ObjectNode value, Context ctx, Collector<String> out) throws Exception {
                JsonNode phone_num = value.get("value").get("phone_num");
                phone_num.asBoolean();
                phone_num.asDouble();
                phone_num.asInt();	
            }
    });

好了,今天的分享就到这里吧,如有不对的地方,欢迎提出宝贵意见,博主也会随时更新的。



版权声明:本文为qq_44962429原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。