Kinetica C# API  Version 7.0.19.0
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Pages
KineticaData.cs
Go to the documentation of this file.
1 using Avro;
2 using Avro.IO;
3 using Avro.Specific;
4 using System;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Reflection;
8 
9 namespace kinetica
10 {
14  public class KineticaData : ISpecificRecord
15  {
16  private RecordSchema m_schema;
17  private PropertyInfo[] m_properties;
18 
22  public Schema Schema
23  {
24  get
25  {
26  return m_schema;
27  }
28  }
29 
35  {
36  m_schema = Avro.Schema.Parse(type.getSchemaString()) as RecordSchema;
37  m_properties = this.GetType().GetProperties();
38  }
39 
44  public KineticaData(System.Type type = null)
45  {
46  var t = type ?? this.GetType();
47  m_schema = SchemaFromType(t, null);
48  m_properties = this.GetType().GetProperties();
49  }
50 
56  public object Get(int fieldPos)
57  {
58  return m_properties[fieldPos].GetValue(this);
59  }
60 
66  public void Put(int fieldPos, object fieldValue)
67  {
68  m_properties[fieldPos].SetValue(this, fieldValue);
69  }
70 
71 
72 
79  public static RecordSchema SchemaFromType( System.Type t, KineticaType ktype = null )
80  {
81  string jsonType = AvroType( t, ktype );
82  return Avro.Schema.Parse( jsonType ) as RecordSchema;
83  }
84 
85 
93  private static string AvroType( System.Type t, KineticaType ktype )
94  {
95  if ( t == null )
96  throw new KineticaException( "Null type passed to AvroType()" );
97 
98  switch ( t.Name)
99  {
100  case "Boolean": return "\"boolean\"";
101  case "Int32": return "\"int\"";
102  case "Int64": return "\"long\"";
103  case "Double": return "\"double\"";
104  case "Single": return "\"float\"";
105  case "Byte[]": return "\"bytes\"";
106  case "String": return "\"string\"";
107  case "String[]": return $"{{ \"type\":\"array\", \"items\":\"string\"}}";
108  case "String[][]": return $"{{ \"type\":\"array\", \"items\":{{ \"type\":\"array\", \"items\":\"string\"}}}}";
109 
110  // For a nullable object, return the avro type of the underlying type (e.g. double)
111  case "Nullable`1": return AvroType( Nullable.GetUnderlyingType( t ), ktype );
112 
113  case "List`1":
114  case "IList`1":
115  if ( t.IsGenericType )
116  {
117  var genericParams = t.GenericTypeArguments;
118  if (1 == genericParams.Length)
119  {
120  return $"{{ \"type\":\"array\", \"items\":{AvroType( genericParams[0], ktype )}}}";
121  }
122  }
123  break;
124 
125  case "Dictionary`2":
126  case "IDictionary`2":
127  if (t.IsGenericType)
128  {
129  var genericParams = t.GenericTypeArguments;
130  if (2 == genericParams.Length)
131  {
132  return $"{{ \"type\":\"map\", \"values\":{AvroType( genericParams[1], ktype )}}}";
133  }
134  }
135  break;
136 
137  // Ignore the "Schema" property inherited from KineticaData
138  case "Schema": break;
139 
140  // Ignore the "RecordSchema" property inherited from KineticaRecord
141  case "RecordSchema":
142  break;
143 
144  // If Type is an object, treat it as a sub-record in Avro
145  default:
146  if (t.IsSubclassOf(typeof(Object)))
147  {
148  string fields = "";
149  // Create the avro string for each property of the class
150  PropertyInfo[] type_properties = t.GetProperties( BindingFlags.DeclaredOnly |
151  BindingFlags.Instance |
152  BindingFlags.Public );
153  Array.Sort( type_properties, delegate ( PropertyInfo p1, PropertyInfo p2 )
154  { return p1.MetadataToken.CompareTo( p2.MetadataToken ); } );
155 
156  foreach ( var prop in type_properties )
157  {
158  bool is_nullable = false;
159  var prop_type = prop.PropertyType;
160  if ( prop_type.IsGenericType && prop_type.GetGenericTypeDefinition() == typeof( Nullable<> ) )
161  { // the property is nullable based on reflection
162  is_nullable = true;
163  }
164  else if ( (ktype != null) && ktype.getColumn( prop.Name ).isNullable() )
165  { // the property is nullable based on information saved in the associated KineticaType
166  is_nullable = true;
167  }
168 
169  // Get the avro type string for the property type
170  string avroType = AvroType( prop_type, ktype );
171  if ( !String.IsNullOrWhiteSpace( avroType ) )
172  {
173  if ( is_nullable )
174  { // the field is nullable
175  fields += $"{{\"name\":\"{prop.Name}\",\"type\":[{avroType},\"null\"]}},";
176  }
177  else
178  { // it's a regular field
179  fields += $"{{\"name\":\"{prop.Name}\",\"type\":{avroType}}},";
180  }
181  }
182  }
183 
184  // Trim the trailing comma from the fields
185  char[] comma = { ',' };
186  fields = fields.TrimEnd( comma );
187 
188  // Put together the avro fields with the name to create a record type
189  return $"{{\"type\":\"record\",\"name\":\"{t.Name}\",\"fields\":[{fields}]}}";
190  }
191  System.Diagnostics.Debug.WriteLine($"Unkonwn type: {t.Name}"); break;
192  }
193 
194  return "";
195  } // end AvroType
196 
197  /* Code to copy current object into a new GenericRecord - Not currently used (or tested)
198  public Avro.Generic.GenericRecord CopyTo()
199  {
200  Avro.Generic.GenericRecord record = new Avro.Generic.GenericRecord(m_schema);
201  foreach (var prop in m_properties)
202  {
203  if (m_schema.Contains(prop.Name))
204  {
205  record.Add(prop.Name, prop.GetValue(this));
206  }
207  }
208 
209  return record;
210  }
211  */
212  }
213 }
static RecordSchema SchemaFromType(System.Type t, KineticaType ktype=null)
Create an Avro Schema from a System.Type and a KineticaType.
Definition: KineticaData.cs:79
KineticaData(System.Type type=null)
Default constructor, with optional System.Type
Definition: KineticaData.cs:44
KineticaData(KineticaType type)
Constructor from Kinetica Type
Definition: KineticaData.cs:34
void Put(int fieldPos, object fieldValue)
Write a specific property to this object
Definition: KineticaData.cs:66
Schema Schema
Avro Schema for this class
Definition: KineticaData.cs:23
KineticaData - class to help with Avro Encoding for Kinetica
Definition: KineticaData.cs:14
object Get(int fieldPos)
Retrieve a specific property from this object
Definition: KineticaData.cs:56