2016-04-10 16 views
7

Ho due schemi simili in cui cambia solo un campo nidificato (è chiamato onefield nello schema1 e anotherfield nello schema2).Unisci due schemi avro in modo programmatico

schema1

{ 
    "type": "record", 
    "name": "event", 
    "namespace": "foo", 
    "fields": [ 
     { 
      "name": "metadata", 
      "type": { 
       "type": "record", 
       "name": "event", 
       "namespace": "foo.metadata", 
       "fields": [ 
        { 
         "name": "onefield", 
         "type": [ 
          "null", 
          "string" 
         ], 
         "default": null 
        } 
       ] 
      }, 
      "default": null 
     } 
    ] 
} 

Schema2

{ 
    "type": "record", 
    "name": "event", 
    "namespace": "foo", 
    "fields": [ 
     { 
      "name": "metadata", 
      "type": { 
       "type": "record", 
       "name": "event", 
       "namespace": "foo.metadata", 
       "fields": [ 
        { 
         "name": "anotherfield", 
         "type": [ 
          "null", 
          "string" 
         ], 
         "default": null 
        } 
       ] 
      }, 
      "default": null 
     } 
    ] 
} 

sono in grado di unire programatically entrambi gli schemi utilizzando Avro 1.8.0:

Schema s1 = new Schema.Parser().parse(schema1); 
Schema s2 = new Schema.Parser().parse(schema2); 
Schema[] schemas = {s1, s2}; 

Schema mergedSchema = null; 
for (Schema schema: schemas) { 
    mergedSchema = AvroStorageUtils.mergeSchema(mergedSchema, schema); 
} 

e usarlo per convertire un json ingresso in una rappresentazione Avro o json:

JsonAvroConverter converter = new JsonAvroConverter(); 
try { 
    byte[] example = new String("{}").getBytes("UTF-8"); 
    byte[] avro = converter.convertToAvro(example, mergedSchema); 
    byte[] json = converter.convertToJson(avro, mergedSchema); 
    System.out.println(new String(json)); 
} catch (AvroConversionException e) { 
    e.printStackTrace(); 
} 

Questo codice mostra i risultati attesi: {"metadata":{"onefield":null,"anotherfield":null}}. Il problema è che non riesco a vedere lo schema unito. Se faccio un semplice System.out.println(mergedSchema) ottengo la seguente eccezione:

Exception in thread "main" org.apache.avro.SchemaParseException: Can't redefine: merged schema (generated by AvroStorage).merged 
    at org.apache.avro.Schema$Names.put(Schema.java:1127) 
    at org.apache.avro.Schema$NamedSchema.writeNameRef(Schema.java:561) 
    at org.apache.avro.Schema$RecordSchema.toJson(Schema.java:689) 
    at org.apache.avro.Schema$RecordSchema.fieldsToJson(Schema.java:715) 
    at org.apache.avro.Schema$RecordSchema.toJson(Schema.java:700) 
    at org.apache.avro.Schema.toString(Schema.java:323) 
    at org.apache.avro.Schema.toString(Schema.java:313) 
    at java.lang.String.valueOf(String.java:2982) 
    at java.lang.StringBuilder.append(StringBuilder.java:131) 

Io lo chiamo il principio di indeterminazione avro :). Sembra che avro sia in grado di lavorare con lo schema unito, ma fallisce quando tenta di serializzare lo schema su JSON. L'unione funziona con schemi più semplici, quindi mi sembra un errore in avro 1.8.0.

Sai cosa potrebbe accadere o come risolverlo? Qualsiasi soluzione alternativa (ad esempio i serializzatori alternativi Schema) è la benvenuta.

+0

Sembra che stia accadendo anche nelle versioni precedenti di avro (1.7.6) http://mail-archives.apache.org/mod_mbox/avro-user/201406.mbox/%[email protected] nabble.com% 3E –

risposta

1

ho trovato lo stesso problema con la classe util maiale ... in realtà ci sono 2 bug qua

  • AVRO permette serializzare dati attraverso GenericDatumWriter utilizzando uno schema valido
  • Il salvadanaio di classe util sta generando schemi non validi perché sta usando lo stesso nome/spazio dei nomi per tutti i campi uniti (istanza di mantenere il nome originale)

Questo funziona correttamente per gli scenari più complessi https://github.com/kite-sdk/kite/blob/master/kite-data/kite-data-core/src/main/java/org/kitesdk/data/spi/SchemaUtil.java#L511

0.123.
Schema mergedSchema = SchemaUtil.merge(s1, s2); 

Dal tuo esempio, sto ottenendo il seguente output

{ 
    "type": "record", 
    "name": "event", 
    "namespace": "foo", 
    "fields": [ 
    { 
     "name": "metadata", 
     "type": { 
     "type": "record", 
     "name": "event", 
     "namespace": "foo.metadata", 
     "fields": [ 
      { 
      "name": "onefield", 
      "type": [ 
       "null", 
       "string" 
      ], 
      "default": null 
      }, 
      { 
      "name": "anotherfield", 
      "type": [ 
       "null", 
       "string" 
      ], 
      "default": null 
      } 
     ] 
     }, 
     "default": null 
    } 
    ] 
} 

Speriamo che questo aiuterà gli altri.

+0

Grazie a @lake. Non sono in grado di provarlo, ma sembra davvero buono. –