背景
Avro可以提供跨语言的数据交互,下面记录如何通过Avro生成java类
步骤
1、定义avsc文件,里面包含类的一些信息,比如包名、类名、属性列表等,示例如下
{
"namespace": "szc.zone",
"type": "record",
"name": "Person",
"fields": [
{
"name": "name",
"type": "string"
},
{
"name": "age",
"type": "int"
},
{
"name": "gender",
"type": "string"
},
{
"name": "hometown",
"type": "string"
}
]
}
其中namespace字段是为包名,name字段是为类名,fields字段是为类包含的属性,里面有属性名和属性类型
2、利用avro-tools工具,生成类文件
先下载avro-tools的jar文件,以版本1.7.7为例,下载地址为http://archive.apache.org/dist/avro/avro-1.7.7/java/,直接下载jar包即可,如下图所示
然后运行命令
java -jar avro-tools-1.7.7.jar compile schema .\input\StringPair.avsc .
其中avro-tools-1.7.7.jar就是下载的jar包,.\input\StringPair.avsc是输入的avsc路径,最后的.是输出目录,也就是当前目录。运行之后的结果如下图所示
在当前目录下,可以看到包目录szc
3、使用
最后把这个包目录整个拷到工程目录里的合适位置,就能使用了
最后,附上生成的Person类的源代码
/**
* Autogenerated by Avro
*
* DO NOT EDIT DIRECTLY
*/
package szc.zone;
@SuppressWarnings("all")
@org.apache.avro.specific.AvroGenerated
public class Person extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Person\",\"namespace\":\"szc.zone\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"age\",\"type\":\"int\"},{\"name\":\"gender\",\"type\":\"string\"},{\"name\":\"hometown\",\"type\":\"string\"}]}");
public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
@Deprecated public CharSequence name;
@Deprecated public int age;
@Deprecated public CharSequence gender;
@Deprecated public CharSequence hometown;
/**
* Default constructor. Note that this does not initialize fields
* to their default values from the schema. If that is desired then
* one should use <code>newBuilder()</code>.
*/
public Person() {}
/**
* All-args constructor.
*/
public Person(CharSequence name, Integer age, CharSequence gender, CharSequence hometown) {
this.name = name;
this.age = age;
this.gender = gender;
this.hometown = hometown;
}
public org.apache.avro.Schema getSchema() { return SCHEMA$; }
// Used by DatumWriter. Applications should not call.
public Object get(int field$) {
switch (field$) {
case 0: return name;
case 1: return age;
case 2: return gender;
case 3: return hometown;
default: throw new org.apache.avro.AvroRuntimeException("Bad index");
}
}
// Used by DatumReader. Applications should not call.
@SuppressWarnings(value="unchecked")
public void put(int field$, Object value$) {
switch (field$) {
case 0: name = (CharSequence)value$; break;
case 1: age = (Integer)value$; break;
case 2: gender = (CharSequence)value$; break;
case 3: hometown = (CharSequence)value$; break;
default: throw new org.apache.avro.AvroRuntimeException("Bad index");
}
}
/**
* Gets the value of the 'name' field.
*/
public CharSequence getName() {
return name;
}
/**
* Sets the value of the 'name' field.
* @param value the value to set.
*/
public void setName(CharSequence value) {
this.name = value;
}
/**
* Gets the value of the 'age' field.
*/
public Integer getAge() {
return age;
}
/**
* Sets the value of the 'age' field.
* @param value the value to set.
*/
public void setAge(Integer value) {
this.age = value;
}
/**
* Gets the value of the 'gender' field.
*/
public CharSequence getGender() {
return gender;
}
/**
* Sets the value of the 'gender' field.
* @param value the value to set.
*/
public void setGender(CharSequence value) {
this.gender = value;
}
/**
* Gets the value of the 'hometown' field.
*/
public CharSequence getHometown() {
return hometown;
}
/**
* Sets the value of the 'hometown' field.
* @param value the value to set.
*/
public void setHometown(CharSequence value) {
this.hometown = value;
}
/** Creates a new Person RecordBuilder */
public static Builder newBuilder() {
return new Builder();
}
/** Creates a new Person RecordBuilder by copying an existing Builder */
public static Builder newBuilder(Builder other) {
return new Builder(other);
}
/** Creates a new Person RecordBuilder by copying an existing Person instance */
public static Builder newBuilder(Person other) {
return new Builder(other);
}
/**
* RecordBuilder for Person instances.
*/
public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<Person>
implements org.apache.avro.data.RecordBuilder<Person> {
private CharSequence name;
private int age;
private CharSequence gender;
private CharSequence hometown;
/** Creates a new Builder */
private Builder() {
super(Person.SCHEMA$);
}
/** Creates a Builder by copying an existing Builder */
private Builder(Builder other) {
super(other);
if (isValidValue(fields()[0], other.name)) {
this.name = data().deepCopy(fields()[0].schema(), other.name);
fieldSetFlags()[0] = true;
}
if (isValidValue(fields()[1], other.age)) {
this.age = data().deepCopy(fields()[1].schema(), other.age);
fieldSetFlags()[1] = true;
}
if (isValidValue(fields()[2], other.gender)) {
this.gender = data().deepCopy(fields()[2].schema(), other.gender);
fieldSetFlags()[2] = true;
}
if (isValidValue(fields()[3], other.hometown)) {
this.hometown = data().deepCopy(fields()[3].schema(), other.hometown);
fieldSetFlags()[3] = true;
}
}
/** Creates a Builder by copying an existing Person instance */
private Builder(Person other) {
super(Person.SCHEMA$);
if (isValidValue(fields()[0], other.name)) {
this.name = data().deepCopy(fields()[0].schema(), other.name);
fieldSetFlags()[0] = true;
}
if (isValidValue(fields()[1], other.age)) {
this.age = data().deepCopy(fields()[1].schema(), other.age);
fieldSetFlags()[1] = true;
}
if (isValidValue(fields()[2], other.gender)) {
this.gender = data().deepCopy(fields()[2].schema(), other.gender);
fieldSetFlags()[2] = true;
}
if (isValidValue(fields()[3], other.hometown)) {
this.hometown = data().deepCopy(fields()[3].schema(), other.hometown);
fieldSetFlags()[3] = true;
}
}
/** Gets the value of the 'name' field */
public CharSequence getName() {
return name;
}
/** Sets the value of the 'name' field */
public Builder setName(CharSequence value) {
validate(fields()[0], value);
this.name = value;
fieldSetFlags()[0] = true;
return this;
}
/** Checks whether the 'name' field has been set */
public boolean hasName() {
return fieldSetFlags()[0];
}
/** Clears the value of the 'name' field */
public Builder clearName() {
name = null;
fieldSetFlags()[0] = false;
return this;
}
/** Gets the value of the 'age' field */
public Integer getAge() {
return age;
}
/** Sets the value of the 'age' field */
public Builder setAge(int value) {
validate(fields()[1], value);
this.age = value;
fieldSetFlags()[1] = true;
return this;
}
/** Checks whether the 'age' field has been set */
public boolean hasAge() {
return fieldSetFlags()[1];
}
/** Clears the value of the 'age' field */
public Builder clearAge() {
fieldSetFlags()[1] = false;
return this;
}
/** Gets the value of the 'gender' field */
public CharSequence getGender() {
return gender;
}
/** Sets the value of the 'gender' field */
public Builder setGender(CharSequence value) {
validate(fields()[2], value);
this.gender = value;
fieldSetFlags()[2] = true;
return this;
}
/** Checks whether the 'gender' field has been set */
public boolean hasGender() {
return fieldSetFlags()[2];
}
/** Clears the value of the 'gender' field */
public Builder clearGender() {
gender = null;
fieldSetFlags()[2] = false;
return this;
}
/** Gets the value of the 'hometown' field */
public CharSequence getHometown() {
return hometown;
}
/** Sets the value of the 'hometown' field */
public Builder setHometown(CharSequence value) {
validate(fields()[3], value);
this.hometown = value;
fieldSetFlags()[3] = true;
return this;
}
/** Checks whether the 'hometown' field has been set */
public boolean hasHometown() {
return fieldSetFlags()[3];
}
/** Clears the value of the 'hometown' field */
public Builder clearHometown() {
hometown = null;
fieldSetFlags()[3] = false;
return this;
}
@Override
public Person build() {
try {
Person record = new Person();
record.name = fieldSetFlags()[0] ? this.name : (CharSequence) defaultValue(fields()[0]);
record.age = fieldSetFlags()[1] ? this.age : (Integer) defaultValue(fields()[1]);
record.gender = fieldSetFlags()[2] ? this.gender : (CharSequence) defaultValue(fields()[2]);
record.hometown = fieldSetFlags()[3] ? this.hometown : (CharSequence) defaultValue(fields()[3]);
return record;
} catch (Exception e) {
throw new org.apache.avro.AvroRuntimeException(e);
}
}
}
}
结语
其实Avro的类生成不是必须的,它只需要我们提供avsc文件作为数据的格式,然后利用DatumWriter+Encoder进行数据输出、DatumReader+Decoder进行数据输入即可。不过,在数据IO的时候,如果要同时对多个字段处理,把字段封装成java类可能有利于进一步操作;但如果只需要处理一两个字段,比如统计计数等,就不用封装了。