blob: e5ef4a2124ce5a2ff181fb9be82558ce6a4de72b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections;
using Avro.IO;
using Avro.Specific;
namespace Avro.Reflect
{
/// <summary>
/// Class for writing data from any specific objects
/// </summary>
public class ReflectDefaultWriter : SpecificDefaultWriter
{
private ClassCache _classCache = new ClassCache();
/// <summary>
/// Class cache
/// </summary>
public ClassCache ClassCache { get => _classCache; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="objType"></param>
/// <param name="schema"></param>
/// <param name="cache"></param>
public ReflectDefaultWriter(Type objType, Schema schema, ClassCache cache)
: base(schema)
{
if (cache != null)
{
_classCache = cache;
}
_classCache.LoadClassCache(objType, schema);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="schema"></param>
public ReflectDefaultWriter(Schema schema)
: base(schema)
{
}
/// <summary>
/// Serialized a record using the given RecordSchema. It uses GetField method
/// to extract the field value from the given object.
/// </summary>
/// <param name="schema">The RecordSchema to use for serialization</param>
/// <param name="value">The value to be serialized</param>
/// <param name="encoder">The Encoder for serialization</param>
protected override void WriteRecord(RecordSchema schema, object value, Encoder encoder)
{
foreach (Field field in schema)
{
try
{
var v = _classCache.GetClass(schema).GetValue(value, field);
Write(field.Schema, v, encoder);
}
catch (Exception ex)
{
throw new AvroException(ex.Message + " in field " + field.Name, ex);
}
}
}
/// <summary>
/// Validates that the record is a fixed record object and that the schema in the object is the
/// same as the given writer schema. Writes the given fixed record into the given encoder
/// </summary>
/// <param name="schema">writer schema</param>
/// <param name="value">fixed object to write</param>
/// <param name="encoder">encoder to write to</param>
protected override void WriteFixed(FixedSchema schema, object value, Encoder encoder)
{
var fixedrec = value as byte[];
if (fixedrec == null)
{
throw new AvroTypeException("Fixed object is not derived from byte[]");
}
if (fixedrec.Length != schema.Size)
{
throw new AvroTypeException($"Fixed object length is not the same as schema length {schema.Size}");
}
encoder.WriteFixed(fixedrec);
}
/// <summary>
/// Serialized an array. The default implementation calls EnsureArrayObject() to ascertain that the
/// given value is an array. It then calls GetArrayLength() and GetArrayElement()
/// to access the members of the array and then serialize them.
/// </summary>
/// <param name="schema">The ArraySchema for serialization</param>
/// <param name="value">The value being serialized</param>
/// <param name="encoder">The encoder for serialization</param>
protected override void WriteArray(ArraySchema schema, object value, Encoder encoder)
{
var arr = value as IEnumerable;
if (arr == null)
{
throw new AvroTypeException("Array does not implement have registered ReflectArray derived type");
}
var arrayHelper = _classCache.GetArrayHelper(schema, (IEnumerable)value);
long l = arrayHelper.Count();
encoder.WriteArrayStart();
encoder.SetItemCount(l);
foreach (var v in arr)
{
encoder.StartItem();
Write(schema.ItemSchema, v, encoder);
}
encoder.WriteArrayEnd();
}
/// <summary>
/// Writes the given map into the given encoder.
/// </summary>
/// <param name="schema">writer schema</param>
/// <param name="value">map to write</param>
/// <param name="encoder">encoder to write to</param>
protected override void WriteMap(MapSchema schema, object value, Encoder encoder)
{
if (value == null)
{
throw new AvroTypeException("Map is null - use a union for nullable types");
}
base.WriteMap(schema, value, encoder);
}
/// <summary>
/// Determines whether an object matches a schema. In the case of enums and records the code looks up the
/// Enum and class caches respectively. Used when writing unions.
/// </summary>
/// <param name="sc"></param>
/// <param name="obj"></param>
/// <returns></returns>
protected override bool Matches(Schema sc, object obj)
{
if (obj == null && sc.Tag != Schema.Type.Null)
{
return false;
}
switch (sc.Tag)
{
case Schema.Type.Null:
return obj == null;
case Schema.Type.Boolean:
return obj is bool;
case Schema.Type.Int:
return obj is int;
case Schema.Type.Long:
return obj is long;
case Schema.Type.Float:
return obj is float;
case Schema.Type.Double:
return obj is double;
case Schema.Type.Bytes:
return obj is byte[];
case Schema.Type.String:
return obj is string;
case Schema.Type.Error:
case Schema.Type.Record:
return _classCache.GetClass(sc as RecordSchema).GetClassType() == obj.GetType();
case Schema.Type.Enumeration:
return EnumCache.GetEnumeration(sc as EnumSchema) == obj.GetType();
case Schema.Type.Array:
return obj is IEnumerable;
case Schema.Type.Map:
return obj is IDictionary;
case Schema.Type.Union:
return false; // Union directly within another union not allowed!
case Schema.Type.Fixed:
return obj is byte[];
case Schema.Type.Logical:
return ((LogicalSchema)sc).LogicalType.IsInstanceOfLogicalType(obj);
default:
throw new AvroException("Unknown schema type: " + sc.Tag);
}
}
}
}