use of com.linkedin.data.schema.UnionDataSchema in project rest.li by linkedin.
the class SchemaToAvroJsonEncoder method encodeFieldType.
/**
* Encode a field's type to an Avro-compliant schema.
*
* Special handling is required for optional fields.
* An optional field is encoded as a union with null.
* If the optional field is not a union, then union
* the field's type with null.
* If the optional field is already a union, then
* include null as a member type if it is not already
* part of the union.
* If the resulting type is a union and resulting field
* has a default value, then the resulting union's list
* of member types are encoded such that the type of
* the translated default value is always the 1st type
* in this list (see Avro specification for more details.)
*
* For required and non-union fields, no special handling is required.
*
* @param field providing the type to encode.
* @throws IOException if there is an error while encoding.
*/
@Override
protected void encodeFieldType(RecordDataSchema.Field field) throws IOException {
boolean optional = field.getOptional();
DataSchema fieldSchema = field.getType();
UnionDataSchema unionDataSchema = (fieldSchema.getDereferencedType() == DataSchema.Type.UNION ? (UnionDataSchema) fieldSchema.getDereferencedDataSchema() : null);
_builder.writeFieldName(TYPE_KEY);
if (optional == false && unionDataSchema == null) {
encode(fieldSchema);
} else {
// special handling for unions
// output will be an union if the field is optional or its type is a union
// whether to add null to translated union,
// set to true for optional non-union type or optional union without null member
boolean addNullMemberType;
// DataSchema of default value, null if there is no default value.
DataSchema defaultValueSchema;
// members of the union (excluding null introduced by optional)
List<DataSchema> resultMemberTypes;
Object defaultValue = field.getDefault();
if (optional) {
if (unionDataSchema == null) {
addNullMemberType = true;
resultMemberTypes = new ArrayList<DataSchema>(1);
resultMemberTypes.add(fieldSchema);
defaultValueSchema = (defaultValue != null && _options.getOptionalDefaultMode() == OptionalDefaultMode.TRANSLATE_DEFAULT ? fieldSchema : DataSchemaConstants.NULL_DATA_SCHEMA);
} else {
addNullMemberType = unionDataSchema.getType(DataSchemaConstants.NULL_TYPE) == null;
resultMemberTypes = unionDataSchema.getTypes();
defaultValueSchema = (defaultValue != null && _options.getOptionalDefaultMode() == OptionalDefaultMode.TRANSLATE_DEFAULT ? unionValueDataSchema(unionDataSchema, defaultValue) : DataSchemaConstants.NULL_DATA_SCHEMA);
}
assert (_options.getOptionalDefaultMode() != OptionalDefaultMode.TRANSLATE_TO_NULL || defaultValueSchema == DataSchemaConstants.NULL_DATA_SCHEMA);
} else {
// must be union
addNullMemberType = false;
resultMemberTypes = unionDataSchema.getTypes();
defaultValueSchema = unionValueDataSchema(unionDataSchema, defaultValue);
}
// encode the member types
// add null member type if addNullMemberType is present
_builder.writeStartArray();
// this variable keeps track of whether null member type has been emitted
boolean emittedNull = false;
// if field has a default, defaultValueSchema != null, always encode it 1st
if (defaultValueSchema != null) {
emittedNull |= (defaultValueSchema.getDereferencedType() == DataSchema.Type.NULL);
encode(defaultValueSchema);
}
for (DataSchema type : resultMemberTypes) {
if (defaultValueSchema == type) {
continue;
}
if (type.getDereferencedType() == DataSchema.Type.NULL) {
if (emittedNull)
continue;
else
emittedNull = true;
}
encode(type);
}
// emit null member type if it is has to be added and has not already been emitted
if (addNullMemberType && emittedNull == false) {
_builder.writeString(DataSchemaConstants.NULL_TYPE);
emittedNull = true;
}
assert (addNullMemberType == false || emittedNull == true);
_builder.writeEndArray();
}
}
use of com.linkedin.data.schema.UnionDataSchema in project rest.li by linkedin.
the class DataElementUtil method element.
/**
* Get the {@link DataElement} by following the specified path starting from
* the provided {@link DataElement}.
*
* @param element provides the {@link DataElement} that is the starting point.
* @param path provides the path components through an {@link Iterable}.
* @return the {@link DataElement} if the path can be followed, else return null.
* @throws IllegalArgumentException if the provided path's syntax is incorrect, Data object does not match provided
* {@link DataSchema}.
*/
public static DataElement element(DataElement element, Iterable<Object> path) throws IllegalArgumentException {
DataElement currentElement = element;
for (Object component : path) {
Object name;
if (currentElement.getValue().getClass() == DataMap.class && component.getClass() != String.class) {
name = component.toString();
} else if (currentElement.getValue().getClass() == DataList.class && component.getClass() != Integer.class) {
try {
name = Integer.parseInt(component.toString());
} catch (NumberFormatException e) {
return null;
}
} else {
name = component;
}
Object childValue = currentElement.getChild(name);
if (childValue == null) {
return null;
}
DataSchema childSchema = null;
DataSchema schema = currentElement.getSchema();
if (schema != null) {
schema = schema.getDereferencedDataSchema();
switch(schema.getType()) {
case ARRAY:
childSchema = ((ArrayDataSchema) schema).getItems();
break;
case MAP:
childSchema = ((MapDataSchema) schema).getValues();
break;
case UNION:
childSchema = ((UnionDataSchema) schema).getType((String) name);
break;
case RECORD:
RecordDataSchema.Field field = ((RecordDataSchema) schema).getField((String) name);
if (field != null) {
childSchema = field.getType();
}
break;
default:
throw new IllegalArgumentException(currentElement.pathAsString() + " has schema of type " + schema.getType() + " which cannot have child, but has child with name \"" + name + "\"");
}
}
currentElement = new SimpleDataElement(childValue, name, childSchema, currentElement);
}
return currentElement;
}
use of com.linkedin.data.schema.UnionDataSchema in project rest.li by linkedin.
the class TestSchemaSampleDataGenerator method testUnionSchema.
@Test
public void testUnionSchema() {
final UnionDataSchema schema = (UnionDataSchema) DataTemplateUtil.getSchema(UnionTest.UnionWithNull.class);
final Set<String> memberKeys = new HashSet<String>();
for (DataSchema memberSchema : schema.getTypes()) {
memberKeys.add(memberSchema.getUnionMemberKey());
}
final String nullMemberKey = DataSchemaConstants.NULL_DATA_SCHEMA.getUnionMemberKey();
for (int i = 0; i < memberKeys.size() * 2; ++i) {
final DataMap value = (DataMap) SchemaSampleDataGenerator.buildData(schema, _spec);
if (value == null) {
Assert.assertTrue(memberKeys.contains(nullMemberKey));
continue;
}
final String key = value.keySet().iterator().next();
Assert.assertTrue(memberKeys.contains(key));
}
}
use of com.linkedin.data.schema.UnionDataSchema in project rest.li by linkedin.
the class TemplateSpecGenerator method generateUnion.
private UnionTemplateSpec generateUnion(UnionDataSchema schema, UnionTemplateSpec unionClass) {
final Map<CustomInfoSpec, Object> customInfoMap = new IdentityHashMap<CustomInfoSpec, Object>(schema.getTypes().size() * 2);
for (DataSchema memberType : schema.getTypes()) {
final UnionTemplateSpec.Member newMember = new UnionTemplateSpec.Member();
unionClass.getMembers().add(newMember);
newMember.setSchema(memberType);
if (memberType.getDereferencedType() != DataSchema.Type.NULL) {
newMember.setClassTemplateSpec(processSchema(memberType, unionClass, memberType.getUnionMemberKey()));
newMember.setDataClass(determineDataClass(memberType, unionClass, memberType.getUnionMemberKey()));
final CustomInfoSpec customInfo = getImmediateCustomInfo(memberType);
if (customInfo != null) {
if (!customInfoMap.containsKey(customInfo)) {
customInfoMap.put(customInfo, null);
}
newMember.setCustomInfo(customInfo);
}
}
}
return unionClass;
}
use of com.linkedin.data.schema.UnionDataSchema in project rest.li by linkedin.
the class TemplateSpecGenerator method processSchema.
private ClassTemplateSpec processSchema(DataSchema schema, ClassTemplateSpec enclosingClass, String memberName) {
final CustomInfoSpec customInfo = getImmediateCustomInfo(schema);
ClassTemplateSpec result = null;
TyperefDataSchema originalTyperefSchema = null;
while (schema.getType() == DataSchema.Type.TYPEREF) {
final TyperefDataSchema typerefSchema = (TyperefDataSchema) schema;
if (originalTyperefSchema == null) {
originalTyperefSchema = typerefSchema;
}
final ClassTemplateSpec found = _schemaToClassMap.get(schema);
schema = typerefSchema.getRef();
if (found == null) {
if (schema.getType() == DataSchema.Type.UNION) {
result = generateUnion((UnionDataSchema) schema, typerefSchema);
break;
} else {
generateTyperef(typerefSchema, originalTyperefSchema);
}
} else if (schema.getType() == DataSchema.Type.UNION) {
result = found;
break;
}
}
if (result == null) {
assert schema == schema.getDereferencedDataSchema();
if (schema instanceof ComplexDataSchema) {
final ClassTemplateSpec found = _schemaToClassMap.get(schema);
if (found == null) {
if (schema instanceof NamedDataSchema) {
result = generateNamedSchema((NamedDataSchema) schema);
} else {
result = generateUnnamedComplexSchema(schema, enclosingClass, memberName);
}
} else {
result = found;
}
if (customInfo != null) {
result = customInfo.getCustomClass();
}
} else if (schema instanceof PrimitiveDataSchema) {
result = (customInfo != null) ? customInfo.getCustomClass() : getPrimitiveClassForSchema((PrimitiveDataSchema) schema, enclosingClass, memberName);
}
}
if (result == null) {
throw unrecognizedSchemaType(enclosingClass, memberName, schema);
}
result.setOriginalTyperefSchema(originalTyperefSchema);
return result;
}
Aggregations