2016-04-10 26 views
7

Mam dwa podobne schematy, w których zmienia się tylko jedno pole zagnieżdżone (nazywa się onefield w schemacie 1 i anotherfield w schemacie2).Scalanie dwóch schematów avro programowo

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 
     } 
    ] 
} 

SCEMA2

{ 
    "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 
     } 
    ] 
} 

jestem w stanie programowo połączyć oba schematy używając 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); 
} 

i użyć go do konwersji na Wejście do json z Avro lub json reprezentacji:

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(); 
} 

Ten kod wskazuje oczekiwany wynik: {"metadata":{"onefield":null,"anotherfield":null}}. Problem polega na tym, że nie widzę scalonego schematu. Jeśli mam zrobić prosty System.out.println(mergedSchema) otrzymuję następujący wyjątek:

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) 

Ja nazywam to avro zasada nieoznaczoności :). Wygląda na to, że avro może pracować z połączonym schematem, ale zawiedzie, gdy próbuje serializować schemat do JSON. Scalanie działa z prostszymi schematami, więc brzmi jak błąd w wersji 1.8.0 dla mnie.

Czy wiesz, co może się dziać lub jak go rozwiązać? Dowolne obejście (na przykład serie alternatywne Schema) są mile widziane.

+0

Wydaje się, że dzieje się to również w poprzednich wersjach avro (1.7.6), http://mail-archives.apache.org/mod_mbox/avro-user/201406.mbox/%[email protected] nabble.com% 3E –

Odpowiedz

1

znalazłem ten sam problem z klasy util świni ... rzeczywiście istnieją 2 bugs tutaj

  • AVRO pozwala szeregować dane przez GenericDatumWriter używając nieprawidłowy schematu
  • skarbonka klasy util generuje nieprawidłowe Schematy ponieważ jest przy użyciu tej samej nazwy/nazw dla wszystkich połączonych pól (instancja od zachować oryginalną nazwę)

to działa prawidłowo w przypadku bardziej złożonych scenariuszy https://github.com/kite-sdk/kite/blob/master/kite-data/kite-data-core/src/main/java/org/kitesdk/data/spi/SchemaUtil.java#L511

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

ze swojego przykładu, ja otrzymuję następujący wynik

{ 
    "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 
    } 
    ] 
} 

Mam nadzieję, że to pomoże innym.

+0

Dzięki @lake. Nie jestem w stanie tego wypróbować, ale wygląda naprawdę dobrze. –