11package com .fasterxml .jackson .dataformat .avro .schema ;
22
3+ import java .util .ArrayList ;
4+ import java .util .List ;
5+
6+ import org .apache .avro .Schema ;
7+ import org .apache .avro .reflect .AvroMeta ;
8+ import org .apache .avro .reflect .AvroSchema ;
9+
310import com .fasterxml .jackson .databind .*;
411import com .fasterxml .jackson .databind .jsonFormatVisitors .JsonFormatVisitable ;
512import com .fasterxml .jackson .databind .jsonFormatVisitors .JsonObjectFormatVisitor ;
613import com .fasterxml .jackson .databind .ser .BeanPropertyWriter ;
714import com .fasterxml .jackson .dataformat .avro .AvroFixedSize ;
8- import org .apache .avro .Schema ;
9-
10- import java .util .ArrayList ;
11- import java .util .List ;
1215
1316public class RecordVisitor
1417 extends JsonObjectFormatVisitor .Base
@@ -18,6 +21,12 @@ public class RecordVisitor
1821
1922 protected final DefinedSchemas _schemas ;
2023
24+ /**
25+ * Tracks if the schema for this record has been overridden (by an annotation or other means), and calls to the {@code property} and
26+ * {@code optionalProperty} methods should be ignored.
27+ */
28+ protected final boolean _overridden ;
29+
2130 protected Schema _avroSchema ;
2231
2332 protected List <Schema .Field > _fields = new ArrayList <Schema .Field >();
@@ -27,16 +36,29 @@ public RecordVisitor(SerializerProvider p, JavaType type, DefinedSchemas schemas
2736 super (p );
2837 _type = type ;
2938 _schemas = schemas ;
30- _avroSchema = Schema .createRecord (AvroSchemaHelper .getName (type ),
31- "Schema for " +type .toCanonical (),
32- AvroSchemaHelper .getNamespace (type ), false );
39+ // Check if the schema for this record is overridden
40+ BeanDescription bean = getProvider ().getConfig ().introspectDirectClassAnnotations (_type );
41+ AvroSchema ann = bean .getClassInfo ().getAnnotation (AvroSchema .class );
42+ if (ann != null ) {
43+ _avroSchema = AvroSchemaHelper .parseJsonSchema (ann .value ());
44+ _overridden = true ;
45+ } else {
46+ _avroSchema = AvroSchemaHelper .initializeRecordSchema (bean );
47+ _overridden = false ;
48+ AvroMeta meta = bean .getClassInfo ().getAnnotation (AvroMeta .class );
49+ if (meta != null ) {
50+ _avroSchema .addProp (meta .key (), meta .value ());
51+ }
52+ }
3353 schemas .addSchema (type , _avroSchema );
3454 }
3555
3656 @ Override
3757 public Schema builtAvroSchema () {
38- // Assumption now is that we are done, so let's assign fields
39- _avroSchema .setFields (_fields );
58+ if (!_overridden ) {
59+ // Assumption now is that we are done, so let's assign fields
60+ _avroSchema .setFields (_fields );
61+ }
4062 return _avroSchema ;
4163 }
4264
@@ -49,14 +71,19 @@ public Schema builtAvroSchema() {
4971 @ Override
5072 public void property (BeanProperty writer ) throws JsonMappingException
5173 {
52- Schema schema = schemaForWriter (writer );
53- _fields .add (new Schema .Field (writer .getName (), schema , null , null ));
74+ if (_overridden ) {
75+ return ;
76+ }
77+ _fields .add (schemaFieldForWriter (writer , false ));
5478 }
5579
5680 @ Override
5781 public void property (String name , JsonFormatVisitable handler ,
5882 JavaType type ) throws JsonMappingException
5983 {
84+ if (_overridden ) {
85+ return ;
86+ }
6087 VisitorFormatWrapperImpl wrapper = new VisitorFormatWrapperImpl (_schemas , getProvider ());
6188 handler .acceptJsonFormatVisitor (wrapper , type );
6289 Schema schema = wrapper .getAvroSchema ();
@@ -65,21 +92,19 @@ public void property(String name, JsonFormatVisitable handler,
6592
6693 @ Override
6794 public void optionalProperty (BeanProperty writer ) throws JsonMappingException {
68- Schema schema = schemaForWriter (writer );
69- /* 23-Nov-2012, tatu: Actually let's also assume that primitive type values
70- * are required, as Jackson does not distinguish whether optional has been
71- * defined, or is merely the default setting.
72- */
73- if (!writer .getType ().isPrimitive ()) {
74- schema = AvroSchemaHelper .unionWithNull (schema );
95+ if (_overridden ) {
96+ return ;
7597 }
76- _fields .add (new Schema . Field (writer . getName (), schema , null , null ));
98+ _fields .add (schemaFieldForWriter (writer , true ));
7799 }
78100
79101 @ Override
80102 public void optionalProperty (String name , JsonFormatVisitable handler ,
81103 JavaType type ) throws JsonMappingException
82104 {
105+ if (_overridden ) {
106+ return ;
107+ }
83108 VisitorFormatWrapperImpl wrapper = new VisitorFormatWrapperImpl (_schemas , getProvider ());
84109 handler .acceptJsonFormatVisitor (wrapper , type );
85110 Schema schema = wrapper .getAvroSchema ();
@@ -95,29 +120,53 @@ public void optionalProperty(String name, JsonFormatVisitable handler,
95120 /**********************************************************************
96121 */
97122
98- protected Schema schemaForWriter (BeanProperty prop ) throws JsonMappingException
123+ protected Schema . Field schemaFieldForWriter (BeanProperty prop , boolean optional ) throws JsonMappingException
99124 {
100- AvroFixedSize fixedSize = prop .getAnnotation (AvroFixedSize .class );
101- if (fixedSize != null ) {
102- return Schema .createFixed (fixedSize .typeName (), null , fixedSize .typeNamespace (), fixedSize .size ());
103- }
125+ Schema writerSchema ;
126+ // Check if schema for property is overridden
127+ AvroSchema schemaOverride = prop .getAnnotation (AvroSchema .class );
128+ if (schemaOverride != null ) {
129+ Schema .Parser parser = new Schema .Parser ();
130+ writerSchema = parser .parse (schemaOverride .value ());
131+ } else {
132+ AvroFixedSize fixedSize = prop .getAnnotation (AvroFixedSize .class );
133+ if (fixedSize != null ) {
134+ writerSchema = Schema .createFixed (fixedSize .typeName (), null , fixedSize .typeNamespace (), fixedSize .size ());
135+ } else {
136+ JsonSerializer <?> ser = null ;
104137
105- JsonSerializer <?> ser = null ;
138+ // 23-Nov-2012, tatu: Ideally shouldn't need to do this but...
139+ if (prop instanceof BeanPropertyWriter ) {
140+ BeanPropertyWriter bpw = (BeanPropertyWriter ) prop ;
141+ ser = bpw .getSerializer ();
142+ }
143+ final SerializerProvider prov = getProvider ();
144+ if (ser == null ) {
145+ if (prov == null ) {
146+ throw JsonMappingException .from (prov , "SerializerProvider missing for RecordVisitor" );
147+ }
148+ ser = prov .findValueSerializer (prop .getType (), prop );
149+ }
150+ VisitorFormatWrapperImpl visitor = new VisitorFormatWrapperImpl (_schemas , prov );
151+ ser .acceptJsonFormatVisitor (visitor , prop .getType ());
152+ writerSchema = visitor .getAvroSchema ();
153+ }
106154
107- // 23-Nov-2012, tatu: Ideally shouldn't need to do this but...
108- if (prop instanceof BeanPropertyWriter ) {
109- BeanPropertyWriter bpw = (BeanPropertyWriter ) prop ;
110- ser = bpw .getSerializer ();
111- }
112- final SerializerProvider prov = getProvider ();
113- if (ser == null ) {
114- if (prov == null ) {
115- throw JsonMappingException .from (prov , "SerializerProvider missing for RecordVisitor" );
155+ /* 23-Nov-2012, tatu: Actually let's also assume that primitive type values
156+ * are required, as Jackson does not distinguish whether optional has been
157+ * defined, or is merely the default setting.
158+ */
159+ if (optional && !prop .getType ().isPrimitive ()) {
160+ writerSchema = AvroSchemaHelper .unionWithNull (writerSchema );
116161 }
117- ser = prov .findValueSerializer (prop .getType (), prop );
118162 }
119- VisitorFormatWrapperImpl visitor = new VisitorFormatWrapperImpl (_schemas , prov );
120- ser .acceptJsonFormatVisitor (visitor , prop .getType ());
121- return visitor .getAvroSchema ();
163+ Schema .Field field = new Schema .Field (prop .getName (), writerSchema , prop .getMetadata ().getDescription (), null );
164+
165+ AvroMeta meta = prop .getAnnotation (AvroMeta .class );
166+ if (meta != null ) {
167+ field .addProp (meta .key (), meta .value ());
168+ }
169+
170+ return field ;
122171 }
123172}
0 commit comments