web-dev-qa-db-fra.com

Comment réparer l'union de démarrage attendue. Vous avez obtenu VALUE_NUMBER_INT lors de la conversion de JSON en Avro sur la ligne de commande?

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".

20
Emre Sevinç

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}
32
Emre Sevinç

Un nouvel encodeur JSON en cours d'élaboration devrait résoudre ce problème courant:

https://issues.Apache.org/jira/browse/AVRO-1582

https://github.com/zolyfarkas/avro

10
ppearcy

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 );
2
Tanmay Naik

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 !

0
Abhinandan Dubey