| // 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 |
| // |
| // http://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.Generic; |
| using System.Globalization; |
| using System.IO; |
| using System.Linq; |
| using System.Numerics; |
| using System.Text; |
| using System.Text.Json; |
| using System.Text.Json.Nodes; |
| using System.Text.Json.Serialization; |
| using System.Threading.Tasks; |
| using Apache.Arrow.Arrays; |
| using Apache.Arrow.Scalars; |
| using Apache.Arrow.Types; |
| |
| namespace Apache.Arrow.IntegrationTest |
| { |
| public class JsonFile |
| { |
| public JsonSchema Schema { get; set; } |
| |
| public List<JsonDictionary> Dictionaries { get; set; } |
| |
| public List<JsonRecordBatch> Batches { get; set; } |
| |
| public static async ValueTask<JsonFile> ParseAsync(FileInfo fileInfo) |
| { |
| using var fileStream = fileInfo.OpenRead(); |
| var options = GetJsonOptions(); |
| return await JsonSerializer.DeserializeAsync<JsonFile>(fileStream, options); |
| } |
| |
| public static JsonFile Parse(FileInfo fileInfo) |
| { |
| using var fileStream = fileInfo.OpenRead(); |
| var options = GetJsonOptions(); |
| return JsonSerializer.Deserialize<JsonFile>(fileStream, options); |
| } |
| |
| public Schema GetSchemaAndDictionaries(out Func<DictionaryType, IArrowArray> dictionaries) |
| { |
| Schema schema = Schema.ToArrow(out Dictionary<DictionaryType, int> dictionaryIndexes); |
| |
| Func<DictionaryType, IArrowArray> lookup = null; |
| lookup = type => Dictionaries.Single(d => d.Id == dictionaryIndexes[type]).Data.ToArrow(type.ValueType, lookup); |
| dictionaries = lookup; |
| |
| return schema; |
| } |
| |
| /// <summary> |
| /// Return both the schema and a specific batch number. |
| /// This method is used by C Data Interface integration testing. |
| /// </summary> |
| public Schema ToArrow(int batchNumber, out RecordBatch batch) |
| { |
| Schema schema = Schema.ToArrow(out Dictionary<DictionaryType, int> dictionaryIndexes); |
| |
| Func<DictionaryType, IArrowArray> lookup = null; |
| lookup = type => Dictionaries.Single(d => d.Id == dictionaryIndexes[type]).Data.ToArrow(type.ValueType, lookup); |
| |
| batch = Batches[batchNumber].ToArrow(schema, lookup); |
| |
| return schema; |
| } |
| |
| private static JsonSerializerOptions GetJsonOptions() |
| { |
| JsonSerializerOptions options = new JsonSerializerOptions() |
| { |
| PropertyNamingPolicy = JsonFileNamingPolicy.Instance, |
| }; |
| options.Converters.Add(new ValidityConverter()); |
| return options; |
| } |
| } |
| |
| public class JsonSchema |
| { |
| public List<JsonField> Fields { get; set; } |
| public JsonMetadata Metadata { get; set; } |
| |
| /// <summary> |
| /// Decode this JSON schema as a Schema instance. |
| /// </summary> |
| public Schema ToArrow(out Dictionary<DictionaryType, int> dictionaryIndexes) |
| { |
| dictionaryIndexes = new Dictionary<DictionaryType, int>(); |
| return CreateSchema(this, dictionaryIndexes); |
| } |
| |
| /// <summary> |
| /// Decode this JSON schema as a Schema instance without computing dictionaries. |
| /// This method is used by C Data Interface integration testing. |
| /// </summary> |
| public Schema ToArrow() |
| { |
| Dictionary<DictionaryType, int> dictionaryIndexes = new Dictionary<DictionaryType, int>(); |
| return CreateSchema(this, dictionaryIndexes); |
| } |
| |
| private static Schema CreateSchema(JsonSchema jsonSchema, Dictionary<DictionaryType, int> dictionaryIndexes) |
| { |
| Schema.Builder builder = new Schema.Builder(); |
| for (int i = 0; i < jsonSchema.Fields.Count; i++) |
| { |
| builder.Field(f => CreateField(f, jsonSchema.Fields[i], dictionaryIndexes)); |
| } |
| |
| if (jsonSchema.Metadata != null) |
| { |
| builder.Metadata(jsonSchema.Metadata); |
| } |
| |
| return builder.Build(); |
| } |
| |
| private static void CreateField(Field.Builder builder, JsonField jsonField, Dictionary<DictionaryType, int> dictionaryIndexes) |
| { |
| Field[] children = null; |
| if (jsonField.Children?.Count > 0) |
| { |
| children = new Field[jsonField.Children.Count]; |
| for (int i = 0; i < jsonField.Children.Count; i++) |
| { |
| Field.Builder field = new Field.Builder(); |
| CreateField(field, jsonField.Children[i], dictionaryIndexes); |
| children[i] = field.Build(); |
| } |
| } |
| |
| IArrowType type = ToArrowType(jsonField.Type, children); |
| |
| if (jsonField.Dictionary != null) |
| { |
| DictionaryType dictType = new DictionaryType( |
| ToArrowType(jsonField.Dictionary.IndexType, new Field[0]), |
| type, |
| jsonField.Dictionary.IsOrdered); |
| |
| dictionaryIndexes[dictType] = jsonField.Dictionary.Id; |
| type = dictType; |
| } |
| |
| builder.Name(jsonField.Name) |
| .DataType(type) |
| .Nullable(jsonField.Nullable); |
| |
| if (jsonField.Metadata != null) |
| { |
| builder.Metadata(jsonField.Metadata); |
| } |
| } |
| |
| private static IArrowType ToArrowType(JsonArrowType type, Field[] children) |
| { |
| return type.Name switch |
| { |
| "bool" => BooleanType.Default, |
| "int" => ToIntArrowType(type), |
| "floatingpoint" => ToFloatingPointArrowType(type), |
| "decimal" => ToDecimalArrowType(type), |
| "binary" => BinaryType.Default, |
| "binaryview" => BinaryViewType.Default, |
| "largebinary" => LargeBinaryType.Default, |
| "utf8" => StringType.Default, |
| "utf8view" => StringViewType.Default, |
| "largeutf8" => LargeStringType.Default, |
| "fixedsizebinary" => new FixedSizeBinaryType(type.ByteWidth), |
| "date" => ToDateArrowType(type), |
| "time" => ToTimeArrowType(type), |
| "duration" => ToDurationArrowType(type), |
| "interval" => ToIntervalArrowType(type), |
| "interval_mdn" => ToIntervalArrowType(type), |
| "timestamp" => ToTimestampArrowType(type), |
| "list" => ToListArrowType(type, children), |
| "listview" => ToListViewArrowType(type, children), |
| "largelist" => ToLargeListArrowType(type, children), |
| "largelistview" => ToLargeListViewArrowType(type, children), |
| "fixedsizelist" => ToFixedSizeListArrowType(type, children), |
| "struct" => ToStructArrowType(type, children), |
| "union" => ToUnionArrowType(type, children), |
| "map" => ToMapArrowType(type, children), |
| "runendencoded" => ToRunEndEncodedArrowType(type, children), |
| "null" => NullType.Default, |
| _ => throw new NotSupportedException($"JsonArrowType not supported: {type.Name}") |
| }; |
| } |
| |
| private static IArrowType ToIntArrowType(JsonArrowType type) |
| { |
| return (type.BitWidth, type.IsSigned) switch |
| { |
| (8, true) => Int8Type.Default, |
| (8, false) => UInt8Type.Default, |
| (16, true) => Int16Type.Default, |
| (16, false) => UInt16Type.Default, |
| (32, true) => Int32Type.Default, |
| (32, false) => UInt32Type.Default, |
| (64, true) => Int64Type.Default, |
| (64, false) => UInt64Type.Default, |
| _ => throw new NotSupportedException($"Int type not supported: {type.BitWidth}, {type.IsSigned}") |
| }; |
| } |
| |
| private static IArrowType ToFloatingPointArrowType(JsonArrowType type) |
| { |
| return type.FloatingPointPrecision switch |
| { |
| "SINGLE" => FloatType.Default, |
| "DOUBLE" => DoubleType.Default, |
| _ => throw new NotSupportedException($"FloatingPoint type not supported: {type.FloatingPointPrecision}") |
| }; |
| } |
| |
| private static IArrowType ToDecimalArrowType(JsonArrowType type) |
| { |
| return type.BitWidth switch |
| { |
| 256 => new Decimal256Type(type.DecimalPrecision, type.Scale), |
| 128 => new Decimal128Type(type.DecimalPrecision, type.Scale), |
| 64 => new Decimal64Type(type.DecimalPrecision, type.Scale), |
| 32 => new Decimal32Type(type.DecimalPrecision, type.Scale), |
| 0 => new Decimal128Type(type.DecimalPrecision, type.Scale), |
| _ => throw new NotSupportedException($"Decimal type not supported. BitWidth: {type.BitWidth}"), |
| }; |
| } |
| |
| private static IArrowType ToDateArrowType(JsonArrowType type) |
| { |
| return type.Unit switch |
| { |
| "DAY" => Date32Type.Default, |
| "MILLISECOND" => Date64Type.Default, |
| _ => throw new NotSupportedException($"Date type not supported: {type.Unit}") |
| }; |
| } |
| |
| private static IArrowType ToTimeArrowType(JsonArrowType type) |
| { |
| return (type.Unit, type.BitWidth) switch |
| { |
| ("SECOND", 32) => new Time32Type(TimeUnit.Second), |
| ("SECOND", 64) => new Time64Type(TimeUnit.Second), |
| ("MILLISECOND", 32) => new Time32Type(TimeUnit.Millisecond), |
| ("MILLISECOND", 64) => new Time64Type(TimeUnit.Millisecond), |
| ("MICROSECOND", 32) => new Time32Type(TimeUnit.Microsecond), |
| ("MICROSECOND", 64) => new Time64Type(TimeUnit.Microsecond), |
| ("NANOSECOND", 32) => new Time32Type(TimeUnit.Nanosecond), |
| ("NANOSECOND", 64) => new Time64Type(TimeUnit.Nanosecond), |
| _ => throw new NotSupportedException($"Time type not supported: {type.Unit}, {type.BitWidth}") |
| }; |
| } |
| |
| private static IArrowType ToDurationArrowType(JsonArrowType type) |
| { |
| return type.Unit switch |
| { |
| "SECOND" => DurationType.Second, |
| "MILLISECOND" => DurationType.Millisecond, |
| "MICROSECOND" => DurationType.Microsecond, |
| "NANOSECOND" => DurationType.Nanosecond, |
| _ => throw new NotSupportedException($"Time type not supported: {type.Unit}, {type.BitWidth}") |
| }; |
| } |
| |
| private static IArrowType ToIntervalArrowType(JsonArrowType type) |
| { |
| return type.Unit switch |
| { |
| "YEAR_MONTH" => IntervalType.YearMonth, |
| "DAY_TIME" => IntervalType.DayTime, |
| "MONTH_DAY_NANO" => IntervalType.MonthDayNanosecond, |
| _ => throw new NotSupportedException($"Interval type not supported: {type.Unit}") |
| }; |
| } |
| |
| private static IArrowType ToTimestampArrowType(JsonArrowType type) |
| { |
| return type.Unit switch |
| { |
| "SECOND" => new TimestampType(TimeUnit.Second, type.Timezone), |
| "MILLISECOND" => new TimestampType(TimeUnit.Millisecond, type.Timezone), |
| "MICROSECOND" => new TimestampType(TimeUnit.Microsecond, type.Timezone), |
| "NANOSECOND" => new TimestampType(TimeUnit.Nanosecond, type.Timezone), |
| _ => throw new NotSupportedException($"Time type not supported: {type.Unit}, {type.BitWidth}") |
| }; |
| } |
| |
| private static IArrowType ToListArrowType(JsonArrowType type, Field[] children) |
| { |
| return new ListType(children[0]); |
| } |
| |
| private static IArrowType ToListViewArrowType(JsonArrowType type, Field[] children) |
| { |
| return new ListViewType(children[0]); |
| } |
| |
| private static IArrowType ToLargeListArrowType(JsonArrowType type, Field[] children) |
| { |
| return new LargeListType(children[0]); |
| } |
| |
| private static IArrowType ToLargeListViewArrowType(JsonArrowType type, Field[] children) |
| { |
| return new LargeListViewType(children[0]); |
| } |
| |
| private static IArrowType ToFixedSizeListArrowType(JsonArrowType type, Field[] children) |
| { |
| return new FixedSizeListType(children[0], type.ListSize); |
| } |
| |
| private static IArrowType ToStructArrowType(JsonArrowType type, Field[] children) |
| { |
| return new StructType(children); |
| } |
| |
| private static IArrowType ToUnionArrowType(JsonArrowType type, Field[] children) |
| { |
| UnionMode mode = type.Mode switch |
| { |
| "SPARSE" => UnionMode.Sparse, |
| "DENSE" => UnionMode.Dense, |
| _ => throw new NotSupportedException($"Union mode not supported: {type.Mode}"), |
| }; |
| return new UnionType(children, type.TypeIds, mode); |
| } |
| |
| private static IArrowType ToMapArrowType(JsonArrowType type, Field[] children) |
| { |
| return new MapType(children[0], type.KeysSorted); |
| } |
| |
| private static IArrowType ToRunEndEncodedArrowType(JsonArrowType type, Field[] children) |
| { |
| return new RunEndEncodedType(children[0], children[1]); |
| } |
| } |
| |
| public class JsonField |
| { |
| public string Name { get; set; } |
| public bool Nullable { get; set; } |
| public JsonArrowType Type { get; set; } |
| public List<JsonField> Children { get; set; } |
| public JsonDictionaryIndex Dictionary { get; set; } |
| public JsonMetadata Metadata { get; set; } |
| } |
| |
| public class JsonArrowType |
| { |
| public string Name { get; set; } |
| |
| // int fields |
| public int BitWidth { get; set; } |
| public bool IsSigned { get; set; } |
| |
| // floating point fields |
| [JsonIgnore] |
| public string FloatingPointPrecision => ExtensionData["precision"].GetString(); |
| |
| // decimal fields |
| [JsonIgnore] |
| public int DecimalPrecision => ExtensionData["precision"].GetInt32(); |
| public int Scale { get; set; } |
| |
| // date and time fields |
| public string Unit { get; set; } |
| // timestamp fields |
| public string Timezone { get; set; } |
| |
| // FixedSizeBinary fields |
| public int ByteWidth { get; set; } |
| |
| // FixedSizeList fields |
| public int ListSize { get; set; } |
| |
| // union fields |
| public string Mode { get; set; } |
| public int[] TypeIds { get; set; } |
| |
| // map fields |
| public bool KeysSorted { get; set; } |
| |
| [JsonExtensionData] |
| public Dictionary<string, JsonElement> ExtensionData { get; set; } |
| } |
| |
| public class JsonDictionaryIndex |
| { |
| public int Id { get; set; } |
| public JsonArrowType IndexType { get; set; } |
| public bool IsOrdered { get; set; } |
| } |
| |
| public class JsonDictionary |
| { |
| public int Id { get; set; } |
| |
| [JsonPropertyName("data")] |
| public JsonRecordBatch Data { get; set; } |
| } |
| |
| public class JsonMetadata : List<KeyValuePair<string, string>> |
| { |
| } |
| |
| public class JsonRecordBatch |
| { |
| public int Count { get; set; } |
| public List<JsonFieldData> Columns { get; set; } |
| |
| /// <summary> |
| /// Decode this JSON record batch as a RecordBatch instance. |
| /// </summary> |
| public RecordBatch ToArrow(Schema schema, Func<DictionaryType, IArrowArray> dictionaries) |
| { |
| return CreateRecordBatch(schema, dictionaries, this); |
| } |
| |
| public IArrowArray ToArrow(IArrowType arrowType, Func<DictionaryType, IArrowArray> dictionaries) |
| { |
| ArrayCreator creator = new ArrayCreator(this.Columns[0], dictionaries); |
| arrowType.Accept(creator); |
| return creator.Array; |
| } |
| |
| private RecordBatch CreateRecordBatch(Schema schema, Func<DictionaryType, IArrowArray> dictionaries, JsonRecordBatch jsonRecordBatch) |
| { |
| if (schema.FieldsList.Count != jsonRecordBatch.Columns.Count) |
| { |
| throw new NotSupportedException($"jsonRecordBatch.Columns.Count '{jsonRecordBatch.Columns.Count}' doesn't match schema field count '{schema.FieldsList.Count}'"); |
| } |
| |
| List<IArrowArray> arrays = new List<IArrowArray>(jsonRecordBatch.Columns.Count); |
| for (int i = 0; i < jsonRecordBatch.Columns.Count; i++) |
| { |
| JsonFieldData data = jsonRecordBatch.Columns[i]; |
| Field field = schema.FieldsList[i]; |
| ArrayCreator creator = new ArrayCreator(data, dictionaries); |
| field.DataType.Accept(creator); |
| arrays.Add(creator.Array); |
| } |
| |
| return new RecordBatch(schema, arrays, jsonRecordBatch.Count); |
| } |
| |
| private class ArrayCreator : |
| IArrowTypeVisitor<BooleanType>, |
| IArrowTypeVisitor<Int8Type>, |
| IArrowTypeVisitor<Int16Type>, |
| IArrowTypeVisitor<Int32Type>, |
| IArrowTypeVisitor<Int64Type>, |
| IArrowTypeVisitor<UInt8Type>, |
| IArrowTypeVisitor<UInt16Type>, |
| IArrowTypeVisitor<UInt32Type>, |
| IArrowTypeVisitor<UInt64Type>, |
| IArrowTypeVisitor<FloatType>, |
| IArrowTypeVisitor<DoubleType>, |
| IArrowTypeVisitor<Decimal32Type>, |
| IArrowTypeVisitor<Decimal64Type>, |
| IArrowTypeVisitor<Decimal128Type>, |
| IArrowTypeVisitor<Decimal256Type>, |
| IArrowTypeVisitor<Date32Type>, |
| IArrowTypeVisitor<Date64Type>, |
| IArrowTypeVisitor<Time32Type>, |
| IArrowTypeVisitor<Time64Type>, |
| IArrowTypeVisitor<DurationType>, |
| IArrowTypeVisitor<IntervalType>, |
| IArrowTypeVisitor<TimestampType>, |
| IArrowTypeVisitor<StringType>, |
| IArrowTypeVisitor<StringViewType>, |
| IArrowTypeVisitor<LargeStringType>, |
| IArrowTypeVisitor<BinaryType>, |
| IArrowTypeVisitor<BinaryViewType>, |
| IArrowTypeVisitor<LargeBinaryType>, |
| IArrowTypeVisitor<FixedSizeBinaryType>, |
| IArrowTypeVisitor<ListType>, |
| IArrowTypeVisitor<ListViewType>, |
| IArrowTypeVisitor<LargeListType>, |
| IArrowTypeVisitor<LargeListViewType>, |
| IArrowTypeVisitor<FixedSizeListType>, |
| IArrowTypeVisitor<StructType>, |
| IArrowTypeVisitor<UnionType>, |
| IArrowTypeVisitor<MapType>, |
| IArrowTypeVisitor<DictionaryType>, |
| IArrowTypeVisitor<RunEndEncodedType>, |
| IArrowTypeVisitor<NullType> |
| { |
| private JsonFieldData JsonFieldData { get; set; } |
| public IArrowArray Array { get; private set; } |
| |
| private readonly Func<DictionaryType, IArrowArray> dictionaries; |
| |
| public ArrayCreator(JsonFieldData jsonFieldData, Func<DictionaryType, IArrowArray> dictionaries) |
| { |
| JsonFieldData = jsonFieldData; |
| this.dictionaries = dictionaries; |
| } |
| |
| public void Visit(BooleanType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer.BitmapBuilder valueBuilder = new ArrowBuffer.BitmapBuilder(validityBuffer.Length); |
| |
| var json = JsonFieldData.Data.GetRawText(); |
| bool[] values = JsonSerializer.Deserialize<bool[]>(json); |
| |
| foreach (bool value in values) |
| { |
| valueBuilder.Append(value); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(); |
| |
| Array = new BooleanArray( |
| valueBuffer, validityBuffer, |
| JsonFieldData.Count, nullCount, 0); |
| } |
| |
| public void Visit(Int8Type type) => GenerateArray<sbyte, Int8Array>((v, n, c, nc, o) => new Int8Array(v, n, c, nc, o)); |
| public void Visit(Int16Type type) => GenerateArray<short, Int16Array>((v, n, c, nc, o) => new Int16Array(v, n, c, nc, o)); |
| public void Visit(Int32Type type) => GenerateArray<int, Int32Array>((v, n, c, nc, o) => new Int32Array(v, n, c, nc, o)); |
| public void Visit(Int64Type type) => GenerateLongArray<long, Int64Array>((v, n, c, nc, o) => new Int64Array(v, n, c, nc, o), s => long.Parse(s)); |
| public void Visit(UInt8Type type) => GenerateArray<byte, UInt8Array>((v, n, c, nc, o) => new UInt8Array(v, n, c, nc, o)); |
| public void Visit(UInt16Type type) => GenerateArray<ushort, UInt16Array>((v, n, c, nc, o) => new UInt16Array(v, n, c, nc, o)); |
| public void Visit(UInt32Type type) => GenerateArray<uint, UInt32Array>((v, n, c, nc, o) => new UInt32Array(v, n, c, nc, o)); |
| public void Visit(UInt64Type type) => GenerateLongArray<ulong, UInt64Array>((v, n, c, nc, o) => new UInt64Array(v, n, c, nc, o), s => ulong.Parse(s)); |
| public void Visit(FloatType type) => GenerateArray<float, FloatArray>((v, n, c, nc, o) => new FloatArray(v, n, c, nc, o)); |
| public void Visit(DoubleType type) => GenerateArray<double, DoubleArray>((v, n, c, nc, o) => new DoubleArray(v, n, c, nc, o)); |
| public void Visit(Time32Type type) => GenerateArray<int, Time32Array>((v, n, c, nc, o) => new Time32Array(type, v, n, c, nc, o)); |
| public void Visit(Time64Type type) => GenerateLongArray<long, Time64Array>((v, n, c, nc, o) => new Time64Array(type, v, n, c, nc, o), s => long.Parse(s)); |
| public void Visit(DurationType type) => GenerateLongArray<long, DurationArray>((v, n, c, nc, o) => new DurationArray(type, v, n, c, nc, o), s => long.Parse(s)); |
| |
| public void Visit(IntervalType type) |
| { |
| switch (type.Unit) |
| { |
| case IntervalUnit.YearMonth: |
| GenerateArray((v, n, c, nc, o) => new YearMonthIntervalArray(v, n, c, nc, o), e => new YearMonthInterval(e.GetInt32())); |
| break; |
| case IntervalUnit.DayTime: |
| GenerateArray( |
| (v, n, c, nc, o) => new DayTimeIntervalArray(v, n, c, nc, o), |
| e => new DayTimeInterval(e.GetProperty("days").GetInt32(), e.GetProperty("milliseconds").GetInt32())); |
| break; |
| case IntervalUnit.MonthDayNanosecond: |
| GenerateArray( |
| (v, n, c, nc, o) => new MonthDayNanosecondIntervalArray(v, n, c, nc, o), |
| e => new MonthDayNanosecondInterval( |
| e.GetProperty("months").GetInt32(), |
| e.GetProperty("days").GetInt32(), |
| e.GetProperty("nanoseconds").GetInt64())); |
| break; |
| default: |
| throw new InvalidOperationException($"unsupported interval unit <{type.Unit}>"); |
| } |
| } |
| |
| public void Visit(Decimal32Type type) |
| { |
| Array = new Decimal32Array(GetDecimalArrayData(type)); |
| } |
| |
| public void Visit(Decimal64Type type) |
| { |
| Array = new Decimal64Array(GetDecimalArrayData(type)); |
| } |
| |
| public void Visit(Decimal128Type type) |
| { |
| Array = new Decimal128Array(GetDecimalArrayData(type)); |
| } |
| |
| public void Visit(Decimal256Type type) |
| { |
| Array = new Decimal256Array(GetDecimalArrayData(type)); |
| } |
| |
| public void Visit(NullType type) |
| { |
| Array = new NullArray(JsonFieldData.Count); |
| } |
| |
| private ArrayData GetDecimalArrayData(FixedSizeBinaryType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| Span<byte> buffer = stackalloc byte[type.ByteWidth]; |
| |
| ArrowBuffer.Builder<byte> valueBuilder = new ArrowBuffer.Builder<byte>(); |
| foreach (string value in values) |
| { |
| buffer.Fill(0); |
| |
| BigInteger bigInteger = BigInteger.Parse(value); |
| if (!bigInteger.TryWriteBytes(buffer, out int bytesWritten, false, !BitConverter.IsLittleEndian)) |
| { |
| throw new InvalidDataException($"Decimal data was too big to fit into {type.BitWidth} bits."); |
| } |
| |
| if (bigInteger.Sign == -1) |
| { |
| buffer.Slice(bytesWritten).Fill(255); |
| } |
| |
| valueBuilder.Append(buffer); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(default); |
| |
| return new ArrayData(type, JsonFieldData.Count, nullCount, 0, new[] { validityBuffer, valueBuffer }); |
| } |
| |
| public void Visit(Date32Type type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| ArrowBuffer.Builder<int> valueBuilder = new ArrowBuffer.Builder<int>(JsonFieldData.Count); |
| var json = JsonFieldData.Data.GetRawText(); |
| int[] values = JsonSerializer.Deserialize<int[]>(json, s_options); |
| |
| foreach (int value in values) |
| { |
| valueBuilder.Append(value); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(); |
| |
| Array = new Date32Array( |
| valueBuffer, validityBuffer, |
| JsonFieldData.Count, nullCount, 0); |
| } |
| |
| public void Visit(Date64Type type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| ArrowBuffer.Builder<long> valueBuilder = new ArrowBuffer.Builder<long>(JsonFieldData.Count); |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| foreach (string value in values) |
| { |
| valueBuilder.Append(long.Parse(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(); |
| |
| Array = new Date64Array( |
| valueBuffer, validityBuffer, |
| JsonFieldData.Count, nullCount, 0); |
| } |
| |
| public void Visit(TimestampType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| ArrowBuffer.Builder<long> valueBuilder = new ArrowBuffer.Builder<long>(JsonFieldData.Count); |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| foreach (string value in values) |
| { |
| valueBuilder.Append(long.Parse(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(); |
| |
| Array = new TimestampArray( |
| type, valueBuffer, validityBuffer, |
| JsonFieldData.Count, nullCount, 0); |
| } |
| |
| public void Visit(StringType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetOffsetBuffer(); |
| |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| ArrowBuffer.Builder<byte> valueBuilder = new ArrowBuffer.Builder<byte>(); |
| foreach (string value in values) |
| { |
| valueBuilder.Append(Encoding.UTF8.GetBytes(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(default); |
| |
| Array = new StringArray(JsonFieldData.Count, offsetBuffer, valueBuffer, validityBuffer, nullCount); |
| } |
| |
| public void Visit(StringViewType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| // ArrowBuffer viewsBuffer = GetViewsBuffer(); |
| ArrowBuffer viewsBuffer = ArrowBuffer.Empty; |
| if (JsonFieldData.Views != null) |
| { |
| ArrowBuffer.Builder<BinaryView> viewBuilder = new ArrowBuffer.Builder<BinaryView>(JsonFieldData.Views.Count); |
| foreach (JsonView jsonView in JsonFieldData.Views) |
| { |
| BinaryView view = (jsonView.BufferIndex == null) ? |
| new BinaryView(Encoding.UTF8.GetBytes(jsonView.Inlined)) : |
| new BinaryView(jsonView.Size, Convert.FromHexString(jsonView.PrefixHex), jsonView.BufferIndex.Value, jsonView.Offset.Value); |
| viewBuilder.Append(view); |
| } |
| viewsBuffer = viewBuilder.Build(); |
| } |
| |
| int bufferCount = JsonFieldData.VariadicDataBuffers?.Count ?? 0; |
| ArrowBuffer[] buffers = new ArrowBuffer[2 + bufferCount]; |
| buffers[0] = validityBuffer; |
| buffers[1] = viewsBuffer; |
| for (int i = 0; i < bufferCount; i++) |
| { |
| buffers[i + 2] = new ArrowBuffer(Convert.FromHexString(JsonFieldData.VariadicDataBuffers[i])).Clone(); |
| } |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, buffers); |
| Array = new StringViewArray(arrayData); |
| } |
| |
| public void Visit(LargeStringType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetLargeOffsetBuffer(); |
| |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| ArrowBuffer.Builder<byte> valueBuilder = new ArrowBuffer.Builder<byte>(); |
| foreach (string value in values) |
| { |
| valueBuilder.Append(Encoding.UTF8.GetBytes(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(default); |
| |
| Array = new LargeStringArray(JsonFieldData.Count, offsetBuffer, valueBuffer, validityBuffer, nullCount); |
| } |
| |
| public void Visit(BinaryType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetOffsetBuffer(); |
| |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| ArrowBuffer.Builder<byte> valueBuilder = new ArrowBuffer.Builder<byte>(); |
| foreach (string value in values) |
| { |
| valueBuilder.Append(ConvertHexStringToByteArray(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(default); |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, new[] { validityBuffer, offsetBuffer, valueBuffer }); |
| Array = new BinaryArray(arrayData); |
| } |
| |
| public void Visit(BinaryViewType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| // ArrowBuffer viewsBuffer = GetViewsBuffer(); |
| ArrowBuffer viewsBuffer = ArrowBuffer.Empty; |
| if (JsonFieldData.Views != null) |
| { |
| ArrowBuffer.Builder<BinaryView> viewBuilder = new ArrowBuffer.Builder<BinaryView>(JsonFieldData.Views.Count); |
| foreach (JsonView jsonView in JsonFieldData.Views) |
| { |
| BinaryView view = (jsonView.BufferIndex == null) ? |
| new BinaryView(Convert.FromHexString(jsonView.Inlined)) : |
| new BinaryView(jsonView.Size, Convert.FromHexString(jsonView.PrefixHex), jsonView.BufferIndex.Value, jsonView.Offset.Value); |
| viewBuilder.Append(view); |
| } |
| viewsBuffer = viewBuilder.Build(); |
| } |
| |
| int bufferCount = JsonFieldData.VariadicDataBuffers?.Count ?? 0; |
| ArrowBuffer[] buffers = new ArrowBuffer[2 + bufferCount]; |
| buffers[0] = validityBuffer; |
| buffers[1] = viewsBuffer; |
| for (int i = 0; i < bufferCount; i++) |
| { |
| buffers[i + 2] = new ArrowBuffer(Convert.FromHexString(JsonFieldData.VariadicDataBuffers[i])).Clone(); |
| } |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, buffers); |
| Array = new BinaryViewArray(arrayData); |
| } |
| |
| public void Visit(LargeBinaryType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetLargeOffsetBuffer(); |
| |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| ArrowBuffer.Builder<byte> valueBuilder = new ArrowBuffer.Builder<byte>(); |
| foreach (string value in values) |
| { |
| valueBuilder.Append(ConvertHexStringToByteArray(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(default); |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, new[] { validityBuffer, offsetBuffer, valueBuffer }); |
| Array = new LargeBinaryArray(arrayData); |
| } |
| |
| public void Visit(FixedSizeBinaryType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json, s_options); |
| |
| ArrowBuffer.Builder<byte> valueBuilder = new ArrowBuffer.Builder<byte>(); |
| foreach (string value in values) |
| { |
| valueBuilder.Append(ConvertHexStringToByteArray(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(default); |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, new[] { validityBuffer, valueBuffer }); |
| Array = new FixedSizeBinaryArray(arrayData); |
| } |
| |
| public void Visit(ListType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetOffsetBuffer(); |
| |
| var data = JsonFieldData; |
| JsonFieldData = data.Children[0]; |
| type.ValueDataType.Accept(this); |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, |
| new[] { validityBuffer, offsetBuffer }, new[] { Array.Data }); |
| Array = new ListArray(arrayData); |
| } |
| |
| public void Visit(ListViewType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetOffsetBuffer(); |
| ArrowBuffer sizeBuffer = GetSizeBuffer(); |
| |
| var data = JsonFieldData; |
| JsonFieldData = data.Children[0]; |
| type.ValueDataType.Accept(this); |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, |
| new[] { validityBuffer, offsetBuffer, sizeBuffer }, new[] { Array.Data }); |
| Array = new ListViewArray(arrayData); |
| } |
| |
| public void Visit(LargeListType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetLargeOffsetBuffer(); |
| |
| var data = JsonFieldData; |
| JsonFieldData = data.Children[0]; |
| type.ValueDataType.Accept(this); |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, |
| new[] { validityBuffer, offsetBuffer }, new[] { Array.Data }); |
| Array = new LargeListArray(arrayData); |
| } |
| |
| public void Visit(LargeListViewType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetLargeOffsetBuffer(); |
| ArrowBuffer sizeBuffer = GetLargeSizeBuffer(); |
| |
| var data = JsonFieldData; |
| JsonFieldData = data.Children[0]; |
| type.ValueDataType.Accept(this); |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, |
| new[] { validityBuffer, offsetBuffer, sizeBuffer }, new[] { Array.Data }); |
| Array = new LargeListViewArray(arrayData); |
| } |
| |
| public void Visit(FixedSizeListType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| var data = JsonFieldData; |
| JsonFieldData = data.Children[0]; |
| type.ValueDataType.Accept(this); |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, |
| new[] { validityBuffer }, new[] { Array.Data }); |
| Array = new FixedSizeListArray(arrayData); |
| } |
| |
| public void Visit(StructType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| ArrayData[] children = new ArrayData[type.Fields.Count]; |
| |
| var data = JsonFieldData; |
| for (int i = 0; i < children.Length; i++) |
| { |
| JsonFieldData = data.Children[i]; |
| type.Fields[i].DataType.Accept(this); |
| children[i] = Array.Data; |
| } |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, |
| new[] { validityBuffer }, children); |
| Array = new StructArray(arrayData); |
| } |
| |
| public void Visit(UnionType type) |
| { |
| ArrowBuffer[] buffers; |
| if (type.Mode == UnionMode.Dense) |
| { |
| buffers = new ArrowBuffer[2]; |
| buffers[1] = GetOffsetBuffer(); |
| } |
| else |
| { |
| buffers = new ArrowBuffer[1]; |
| } |
| buffers[0] = GetTypeIdBuffer(); |
| |
| ArrayData[] children = GetChildren(type); |
| |
| int nullCount = 0; |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, buffers, children); |
| Array = UnionArray.Create(arrayData); |
| } |
| |
| public void Visit(MapType type) |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| ArrowBuffer offsetBuffer = GetOffsetBuffer(); |
| |
| var data = JsonFieldData; |
| JsonFieldData = data.Children[0]; |
| type.KeyValueType.Accept(this); |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData(type, JsonFieldData.Count, nullCount, 0, |
| new[] { validityBuffer, offsetBuffer }, new[] { Array.Data }); |
| Array = new MapArray(arrayData); |
| } |
| |
| public void Visit(DictionaryType type) |
| { |
| type.IndexType.Accept(this); |
| Array = new DictionaryArray(type, Array, this.dictionaries(type)); |
| } |
| |
| public void Visit(RunEndEncodedType type) |
| { |
| var data = JsonFieldData; |
| |
| JsonFieldData = data.Children[0]; |
| type.RunEndsDataType.Accept(this); |
| ArrayData runEndsData = Array.Data; |
| |
| JsonFieldData = data.Children[1]; |
| type.ValuesDataType.Accept(this); |
| ArrayData valuesData = Array.Data; |
| |
| JsonFieldData = data; |
| |
| ArrayData arrayData = new ArrayData( |
| type, JsonFieldData.Count, nullCount: 0, offset: 0, |
| buffers: System.Array.Empty<ArrowBuffer>(), |
| children: new[] { runEndsData, valuesData }); |
| Array = new RunEndEncodedArray(arrayData); |
| } |
| |
| private ArrayData[] GetChildren(NestedType type) |
| { |
| ArrayData[] children = new ArrayData[type.Fields.Count]; |
| |
| var data = JsonFieldData; |
| for (int i = 0; i < children.Length; i++) |
| { |
| JsonFieldData = data.Children[i]; |
| type.Fields[i].DataType.Accept(this); |
| children[i] = Array.Data; |
| } |
| JsonFieldData = data; |
| |
| return children; |
| } |
| |
| private static byte[] ConvertHexStringToByteArray(string hexString) |
| { |
| byte[] data = new byte[hexString.Length / 2]; |
| for (int index = 0; index < data.Length; index++) |
| { |
| data[index] = byte.Parse(hexString.AsSpan(index * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); |
| } |
| |
| return data; |
| } |
| |
| private static readonly JsonSerializerOptions s_options = new JsonSerializerOptions() |
| { |
| Converters = |
| { |
| new ByteArrayConverter() |
| } |
| }; |
| |
| private void GenerateArray<T, TArray>(Func<ArrowBuffer, ArrowBuffer, int, int, int, TArray> createArray) |
| where T : struct, IEquatable<T> |
| where TArray : PrimitiveArray<T> |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| ArrowBuffer.Builder<T> valueBuilder = new ArrowBuffer.Builder<T>(JsonFieldData.Count); |
| var json = JsonFieldData.Data.GetRawText(); |
| T[] values = JsonSerializer.Deserialize<T[]>(json, s_options); |
| |
| foreach (T value in values) |
| { |
| valueBuilder.Append(value); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(); |
| |
| Array = createArray( |
| valueBuffer, validityBuffer, |
| JsonFieldData.Count, nullCount, 0); |
| } |
| |
| private void GenerateLongArray<T, TArray>(Func<ArrowBuffer, ArrowBuffer, int, int, int, TArray> createArray, Func<string, T> parse) |
| where T : struct, IEquatable<T> |
| where TArray : PrimitiveArray<T> |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| ArrowBuffer.Builder<T> valueBuilder = new ArrowBuffer.Builder<T>(JsonFieldData.Count); |
| var json = JsonFieldData.Data.GetRawText(); |
| string[] values = JsonSerializer.Deserialize<string[]>(json); |
| |
| foreach (string value in values) |
| { |
| valueBuilder.Append(parse(value)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(); |
| |
| Array = createArray( |
| valueBuffer, validityBuffer, |
| JsonFieldData.Count, nullCount, 0); |
| } |
| |
| private void GenerateArray<T, TArray>(Func<ArrowBuffer, ArrowBuffer, int, int, int, TArray> createArray, Func<JsonElement, T> construct) |
| where T : struct, IEquatable<T> |
| where TArray : PrimitiveArray<T> |
| { |
| ArrowBuffer validityBuffer = GetValidityBuffer(out int nullCount); |
| |
| ArrowBuffer.Builder<T> valueBuilder = new ArrowBuffer.Builder<T>(JsonFieldData.Count); |
| |
| foreach (JsonElement element in JsonFieldData.Data.EnumerateArray()) |
| { |
| valueBuilder.Append(construct(element)); |
| } |
| ArrowBuffer valueBuffer = valueBuilder.Build(); |
| |
| Array = createArray( |
| valueBuffer, validityBuffer, |
| JsonFieldData.Count, nullCount, 0); |
| } |
| |
| private ArrowBuffer GetOffsetBuffer() |
| { |
| ArrowBuffer.Builder<int> valueOffsets = new ArrowBuffer.Builder<int>(JsonFieldData.Offset.Count); |
| valueOffsets.AppendRange(JsonFieldData.IntOffset); |
| return valueOffsets.Build(default); |
| } |
| |
| private ArrowBuffer GetLargeOffsetBuffer() |
| { |
| ArrowBuffer.Builder<long> valueOffsets = new ArrowBuffer.Builder<long>(JsonFieldData.Offset.Count); |
| valueOffsets.AppendRange(JsonFieldData.LongOffset); |
| return valueOffsets.Build(default); |
| } |
| |
| private ArrowBuffer GetSizeBuffer() |
| { |
| ArrowBuffer.Builder<int> valueSizes = new ArrowBuffer.Builder<int>(JsonFieldData.Size.Count); |
| valueSizes.AppendRange(JsonFieldData.IntSize); |
| return valueSizes.Build(default); |
| } |
| |
| private ArrowBuffer GetLargeSizeBuffer() |
| { |
| ArrowBuffer.Builder<long> valueSizes = new ArrowBuffer.Builder<long>(JsonFieldData.Size.Count); |
| valueSizes.AppendRange(JsonFieldData.LongSize); |
| return valueSizes.Build(default); |
| } |
| |
| private ArrowBuffer GetTypeIdBuffer() |
| { |
| ArrowBuffer.Builder<byte> typeIds = new ArrowBuffer.Builder<byte>(JsonFieldData.TypeId.Length); |
| for (int i = 0; i < JsonFieldData.TypeId.Length; i++) |
| { |
| typeIds.Append(checked((byte)JsonFieldData.TypeId[i])); |
| } |
| return typeIds.Build(default); |
| } |
| |
| private ArrowBuffer GetValidityBuffer(out int nullCount) |
| { |
| if (JsonFieldData.Validity == null) |
| { |
| nullCount = 0; |
| return ArrowBuffer.Empty; |
| } |
| |
| ArrowBuffer.BitmapBuilder validityBuilder = new ArrowBuffer.BitmapBuilder(JsonFieldData.Validity.Length); |
| validityBuilder.AppendRange(JsonFieldData.Validity); |
| |
| nullCount = validityBuilder.UnsetBitCount; |
| return validityBuilder.Build(); |
| } |
| |
| public void Visit(IArrowType type) |
| { |
| throw new NotImplementedException($"{type.Name} not implemented"); |
| } |
| } |
| } |
| |
| public class JsonFieldData |
| { |
| public string Name { get; set; } |
| public int Count { get; set; } |
| public bool[] Validity { get; set; } |
| public JsonArray Offset { get; set; } |
| |
| [JsonPropertyName("SIZE")] |
| public JsonArray Size { get; set; } |
| public int[] TypeId { get; set; } |
| public JsonElement Data { get; set; } |
| public List<JsonFieldData> Children { get; set; } |
| |
| [JsonPropertyName("VIEWS")] |
| public List<JsonView> Views { get; set; } |
| |
| [JsonPropertyName("VARIADIC_DATA_BUFFERS")] |
| public List<string> VariadicDataBuffers { get; set; } |
| |
| [JsonIgnore] |
| public IEnumerable<int> IntOffset |
| { |
| get { return Offset.Select(GetInt); } |
| } |
| |
| [JsonIgnore] |
| public IEnumerable<long> LongOffset |
| { |
| get { return Offset.Select(GetLong); } |
| } |
| |
| [JsonIgnore] |
| public IEnumerable<int> IntSize |
| { |
| get { return Size.Select(GetInt); } |
| } |
| |
| [JsonIgnore] |
| public IEnumerable<long> LongSize |
| { |
| get { return Size.Select(GetLong); } |
| } |
| |
| static int GetInt(JsonNode node) |
| { |
| try |
| { |
| return node.GetValue<int>(); |
| } |
| catch |
| { |
| return int.Parse(node.GetValue<string>()); |
| } |
| } |
| |
| static long GetLong(JsonNode node) |
| { |
| try |
| { |
| return node.GetValue<long>(); |
| } |
| catch |
| { |
| return long.Parse(node.GetValue<string>()); |
| } |
| } |
| } |
| |
| public class JsonView |
| { |
| [JsonPropertyName("SIZE")] |
| public int Size { get; set; } |
| |
| [JsonPropertyName("INLINED")] |
| public string Inlined { get; set; } |
| |
| [JsonPropertyName("PREFIX_HEX")] |
| public string PrefixHex { get; set; } |
| |
| [JsonPropertyName("BUFFER_INDEX")] |
| public int? BufferIndex { get; set; } |
| |
| [JsonPropertyName("OFFSET")] |
| public int? Offset { get; set; } |
| } |
| |
| internal sealed class ValidityConverter : JsonConverter<bool> |
| { |
| public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) |
| { |
| if (reader.TokenType == JsonTokenType.True) return true; |
| if (reader.TokenType == JsonTokenType.False) return false; |
| |
| if (typeToConvert != typeof(bool) || reader.TokenType != JsonTokenType.Number) |
| { |
| throw new InvalidOperationException($"Unexpected bool data: {reader.TokenType}"); |
| } |
| |
| int value = reader.GetInt32(); |
| if (value == 0) return false; |
| if (value == 1) return true; |
| |
| throw new InvalidOperationException($"Unexpected bool value: {value}"); |
| } |
| |
| public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) => throw new NotImplementedException(); |
| } |
| |
| internal sealed class ByteArrayConverter : JsonConverter<byte[]> |
| { |
| public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) |
| { |
| if (reader.TokenType != JsonTokenType.StartArray) |
| { |
| throw new InvalidOperationException($"Unexpected byte[] token: {reader.TokenType}"); |
| } |
| |
| List<byte> values = new List<byte>(); |
| while (reader.Read()) |
| { |
| if (reader.TokenType == JsonTokenType.EndArray) |
| { |
| return values.ToArray(); |
| } |
| |
| if (reader.TokenType != JsonTokenType.Number) |
| { |
| throw new InvalidOperationException($"Unexpected byte token: {reader.TokenType}"); |
| } |
| |
| values.Add(reader.GetByte()); |
| } |
| |
| throw new InvalidOperationException("Unexpectedly reached the end of the reader"); |
| } |
| |
| public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) => throw new NotImplementedException(); |
| } |
| |
| internal sealed class JsonFileNamingPolicy : JsonNamingPolicy |
| { |
| public static JsonFileNamingPolicy Instance { get; } = new JsonFileNamingPolicy(); |
| |
| public override string ConvertName(string name) |
| { |
| if (name == "Validity") |
| { |
| return "VALIDITY"; |
| } |
| else if (name == "Offset") |
| { |
| return "OFFSET"; |
| } |
| else if (name == "TypeId") |
| { |
| return "TYPE_ID"; |
| } |
| else if (name == "Data") |
| { |
| return "DATA"; |
| } |
| else |
| { |
| return CamelCase.ConvertName(name); |
| } |
| } |
| } |
| } |