JSON是一种使用较广的半结构化数据格式。读取JSON数据的最简单的方式是将数据作为文本文件读取,然后使用JSON解析器来对RDD中的值进行映射操作。类似地,也可以使用我们喜欢的JSON序列化库来将数据转为字符串,然后将其写出去。在Java和Scala中也可以使用一个自定义Hadoop格式来操作JSON数据。
读取JSON
将数据作为文本文件读取,然后对JSON数据进行解析,这样的方法可以在所有支持的编程语言中使用。这种方法假设文件中的每一行都是一条JSON记录。如果你有跨行的JSON数据,你就只能读入整个文件,然后对每个文件进行解析。如果在你使用的语言中构建一个JSON解析器的开销较大,你可以使用mapPartitions()来重用解析器。
我们使用的这三种编程语言中有大量可用的JSON库,在Java中会使用Jackson。使用简单,性能还不错
/**
* @author DKing
* @description
* @date 2019/6/16
*/
public class JsonProcessor {
public static void main(String[] args) {
class ParseJson implements FlatMapFunction<Iterator<String>, Person> {
@Override
public Iterator<Person> call(Iterator<String> stringIterator) throws Exception {
ArrayList<Person> people = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
while (stringIterator.hasNext()) {
String line = stringIterator.next();
try {
people.add(mapper.readValue(line, Person.class));
} catch (Exception e) {
// 跳过失败的数据
}
}
return people.iterator();
}
}
SparkConf conf = new SparkConf().setMaster("local").setAppName("TextFile");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> input = sc.textFile("file.json");
JavaRDD<Person> result = input.mapPartitions(new ParseJson());
}
}
处理格式不正确的记录有可能会引起很严重的问题,尤其对于像JSON这样的半结构化数据来说。对于小数据集来说,可以接受在遇到错误的输入时停止程序,但是对于大规模数据集来说,格式错误是家常便饭。如果选择跳过格式不正确的数据,你应该尝试使用累加器来跟踪错误的个数。
保存JSON
写出JSON文件比读取它要简单得多,因为不需要考虑格式错误的数据,并且也知道要写出的数据的类型。可以使用之前将字符串RDD转为解析好的JSON数据的库,将由结构化数据组成的RDD转为字符串RDD,然后使用Spark的文本文件API写出去。
假设我们要选出喜爱熊猫的人,就可以从第一步中获取输入数据,然后筛选出喜爱熊猫的人。
/**
* @author DKing
* @description
* @date 2019/6/16
*/
public class JsonProcessor {
public static void main(String[] args) {
class WriteJson implements FlatMapFunction<Iterator<Person>, String> {
@Override
public Iterator<String> call(Iterator<Person> personIterator) throws Exception {
ArrayList<String> text = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
while (personIterator.hasNext()) {
Person person = personIterator.next();
text.add(mapper.writeValueAsString(person));
}
return text.iterator();
}
}
class ParseJson implements FlatMapFunction<Iterator<String>, Person> {
@Override
public Iterator<Person> call(Iterator<String> stringIterator) throws Exception {
ArrayList<Person> people = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
while (stringIterator.hasNext()) {
String line = stringIterator.next();
try {
people.add(mapper.readValue(line, Person.class));
} catch (Exception e) {
// 跳过失败的数据
}
}
return people.iterator();
}
}
SparkConf conf = new SparkConf().setMaster("local").setAppName("TextFile");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> input = sc.textFile("file.json");
JavaRDD<Person> result = input.mapPartitions(new ParseJson()).filter(
new LikesPandas());
JavaRDD<String> formatted = result.mapPartitions(new WriteJson());
String outfile = "/opt/getPandasLiker";
formatted.saveAsTextFile(outfile);
}
}
这样一来,就可以通过已有的操作文本数据的机制和JSON库,使用Spark轻易地读取和保存JSON数据了。