J'essaie de valider un fichier JSON à l'aide d'un schéma Avro et d'écrire le fichier Avro correspondant. Tout d'abord, j'ai défini le schéma Avro suivant nommé user.avsc
:
{"namespace": "example.avro",
"type": "record",
"name": "user",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}
Puis créé un user.json
fichier:
{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
Et puis j'ai essayé de courir:
Java -jar ~/bin/avro-tools-1.7.7.jar fromjson --schema-file user.avsc user.json > user.avro
Mais je reçois l'exception suivante:
Exception in thread "main" org.Apache.avro.AvroTypeException: Expected start-union. Got VALUE_NUMBER_INT
at org.Apache.avro.io.JsonDecoder.error(JsonDecoder.Java:697)
at org.Apache.avro.io.JsonDecoder.readIndex(JsonDecoder.Java:441)
at org.Apache.avro.io.ResolvingDecoder.doAction(ResolvingDecoder.Java:290)
at org.Apache.avro.io.parsing.Parser.advance(Parser.Java:88)
at org.Apache.avro.io.ResolvingDecoder.readIndex(ResolvingDecoder.Java:267)
at org.Apache.avro.generic.GenericDatumReader.read(GenericDatumReader.Java:155)
at org.Apache.avro.generic.GenericDatumReader.readField(GenericDatumReader.Java:193)
at org.Apache.avro.generic.GenericDatumReader.readRecord(GenericDatumReader.Java:183)
at org.Apache.avro.generic.GenericDatumReader.read(GenericDatumReader.Java:151)
at org.Apache.avro.generic.GenericDatumReader.read(GenericDatumReader.Java:142)
at org.Apache.avro.tool.DataFileWriteTool.run(DataFileWriteTool.Java:99)
at org.Apache.avro.tool.Main.run(Main.Java:84)
at org.Apache.avro.tool.Main.main(Main.Java:73)
Suis-je en train de manquer quelque chose? Pourquoi est-ce que j'obtiens "Union de démarrage attendue. J'ai obtenu VALUE_NUMBER_INT".
Selon l'explication de Doug Cutting ,
Le codage JSON d'Avro nécessite que les valeurs d'union non nulles soient marquées avec leur type prévu. Cela est dû au fait que les unions comme ["bytes", "string"] et ["int", "long"] sont ambiguës dans JSON, les premières sont toutes deux codées en tant que chaînes JSON, tandis que les secondes sont toutes deux codées en tant que nombres JSON.
http://avro.Apache.org/docs/current/spec.html#json_encoding
Ainsi, votre enregistrement doit être codé comme suit:
{"name": "Alyssa", "favorite_number": {"int": 7}, "favorite_color": null}
Un nouvel encodeur JSON en cours d'élaboration devrait résoudre ce problème courant:
J'ai implémenté union et sa validation, il suffit de créer un schéma d'union et de transmettre ses valeurs via postman. URL de resgistry est l'URL que vous spécifiez pour les propriétés de kafka, u peut également transmettre des valeurs dynamiques à votre schéma
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> response = template.exchange(""+registryUrl+"/subjects/"+topic+"/versions/"+version+"", HttpMethod.GET, entity, String.class);
String responseData = response.getBody();
JSONObject jsonObject = new JSONObject(responseData);
JSONObject jsonObjectResult = new JSONObject(jsonResult);
String getData = jsonObject.get("schema").toString();
Schema.Parser parser = new Schema.Parser();
Schema schema = parser.parse(getData);
GenericRecord genericRecord = new GenericData.Record(schema);
schema.getFields().stream().forEach(field->{
genericRecord.put(field.name(),jsonObjectResult.get(field.name()));
});
GenericDatumReader<GenericRecord>reader = new GenericDatumReader<GenericRecord>(schema);
boolean data = reader.getData().validate(schema,genericRecord );
Comme l'a souligné @ Emre-Sevinc, le problème concerne l'encodage de votre enregistrement Avro.
Pour être plus précis ici;
Ne faites pas ça:
jsonRecord = avroGenericRecord.toString
Au lieu de cela, procédez comme suit:
val writer = new GenericDatumWriter[GenericRecord](avroSchema)
val baos = new ByteArrayOutputStream
val jsonEncoder = EncoderFactory.get.jsonEncoder(avroSchema, baos)
writer.write(avroGenericRecord, jsonEncoder)
jsonEncoder.flush
val jsonRecord = baos.toString("UTF-8")
Vous aurez également besoin des importations suivantes:
import org.Apache.avro.Schema
import org.Apache.avro.generic.{GenericData, GenericDatumReader, GenericDatumWriter, GenericRecord}
import org.Apache.avro.io.{DecoderFactory, EncoderFactory}
Après cela, vous obtiendrez jsonRecord
avec des valeurs d'union non nul marquées avec leur type prévu.
J'espère que cela t'aides !