Skip to content

Commit

Permalink
Remove cache in schema generator (#157)
Browse files Browse the repository at this point in the history
* Remove cache in schema generator

* fix
  • Loading branch information
AdrianStrugala committed Apr 19, 2024
1 parent 06a285d commit 17b6146
Show file tree
Hide file tree
Showing 22 changed files with 584 additions and 555 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,13 @@ internal TypeSchema BuildSchema(Type type)
uint currentDepth,
Type prioritizedType = null,
MemberInfo memberInfo = null)
{
{
if (currentDepth == _options.MaxItemsInSchemaTree)
{
throw new SerializationException(string.Format(CultureInfo.InvariantCulture, "Maximum depth of object graph reached."));
}

if (_hasCustomConverters)
if (_hasCustomConverters)
{
if (_customSchemaMapping.TryGetValue(type, out var schema))
{
Expand Down
8 changes: 4 additions & 4 deletions src/AvroConvert/AvroObjectServices/BuildSchema/Schema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,18 @@ public override string ToString()
using (var result = new StringWriter(CultureInfo.InvariantCulture))
{
var writer = new JsonTextWriter(result);
this.ToJson(writer, new HashSet<NamedSchema>());
this.ToJson(writer);
return result.ToString();
}
}

internal void ToJson(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal void ToJson(JsonTextWriter writer)
{
this.ToJsonSafe(writer, seenSchemas);
this.ToJsonSafe(writer);
}


internal abstract void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas);
internal abstract void ToJsonSafe(JsonTextWriter writer);


internal static TypeSchema Create(string schemaInJson)
Expand Down
73 changes: 25 additions & 48 deletions src/AvroConvert/AvroObjectServices/BuildSchema/TypeSchemaBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json.Linq;
using SolTechnology.Avro.AvroObjectServices.FileHeader;
using SolTechnology.Avro.AvroObjectServices.Schemas;
using SolTechnology.Avro.AvroObjectServices.Schemas.Abstract;
using SolTechnology.Avro.AvroObjectServices.Schemas.AvroTypes;
using SolTechnology.Avro.Infrastructure.Attributes;
using SolTechnology.Avro.Infrastructure.Extensions;

Expand Down Expand Up @@ -70,46 +68,35 @@ internal TypeSchema BuildSchema(string schema)
string.Format(CultureInfo.InvariantCulture, "'{0}' is invalid JSON.", schema));
}

return this.Parse(token, null, new Dictionary<string, NamedSchema>());
return this.Parse(token, null);
}

/// <summary>
/// Parses the specified token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="parent">The parent schema.</param>
/// <param name="namedSchemas">The schemas.</param>
/// <returns>
/// Schema internal representation.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when JSON schema type is not supported.</exception>
private TypeSchema Parse(JToken token, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas)
private TypeSchema Parse(JToken token, NamedSchema parent)
{
if (token.Type == JTokenType.Object)
{
return this.ParseJsonObject(token as JObject, parent, namedSchemas);
return this.ParseJsonObject(token as JObject, parent);
}

if (token.Type == JTokenType.String)
{
var t = (string)token;
if (namedSchemas.ContainsKey(t))
{
return namedSchemas[t];
}

if (parent != null && namedSchemas.ContainsKey(parent.Namespace + "." + t))
{
return namedSchemas[parent.Namespace + "." + t];
}

// Primitive.

return this.ParsePrimitiveTypeFromString(t);
}

if (token.Type == JTokenType.Array)
{
return this.ParseUnionType(token as JArray, parent, namedSchemas);
return this.ParseUnionType(token as JArray, parent);
}

throw new SerializationException(
Expand All @@ -121,12 +108,11 @@ private TypeSchema Parse(JToken token, NamedSchema parent, Dictionary<string, Na
/// </summary>
/// <param name="token">The object.</param>
/// <param name="parent">The parent schema.</param>
/// <param name="namedSchemas">The schemas.</param>
/// <returns>
/// Schema internal representation.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when JSON schema type is invalid.</exception>
private TypeSchema ParseJsonObject(JObject token, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas)
private TypeSchema ParseJsonObject(JObject token, NamedSchema parent)
{
JToken tokenType = token[AvroKeywords.Type];
if (tokenType.Type == JTokenType.String)
Expand All @@ -137,19 +123,19 @@ private TypeSchema ParseJsonObject(JObject token, NamedSchema parent, Dictionary
var logicalType = token.OptionalProperty<string>(AvroKeywords.LogicalType);
if (logicalType != null)
{
return this.ParseLogicalType(token, parent, namedSchemas, logicalType);
return this.ParseLogicalType(token, parent, logicalType);
}

switch (type)
{
case AvroType.Record:
return this.ParseRecordType(token, parent, namedSchemas);
return this.ParseRecordType(token, parent);
case AvroType.Enum:
return this.ParseEnumType(token, parent, namedSchemas);
return this.ParseEnumType(token, parent);
case AvroType.Array:
return this.ParseArrayType(token, parent, namedSchemas);
return this.ParseArrayType(token, parent);
case AvroType.Map:
return this.ParseMapType(token, parent, namedSchemas);
return this.ParseMapType(token, parent);
case AvroType.Fixed:
return this.ParseFixedType(token, parent);
default:
Expand All @@ -167,7 +153,7 @@ private TypeSchema ParseJsonObject(JObject token, NamedSchema parent, Dictionary

if (tokenType.Type == JTokenType.Array)
{
return this.ParseUnionType(tokenType as JArray, parent, namedSchemas);
return this.ParseUnionType(tokenType as JArray, parent);
}

throw new SerializationException(
Expand All @@ -184,13 +170,13 @@ private TypeSchema ParseJsonObject(JObject token, NamedSchema parent, Dictionary
/// Schema internal representation.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when union schema type is invalid.</exception>
private TypeSchema ParseUnionType(JArray unionToken, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas)
private TypeSchema ParseUnionType(JArray unionToken, NamedSchema parent)
{
var types = new HashSet<string>();
var schemas = new List<TypeSchema>();
foreach (var typeAlternative in unionToken.Children())
{
var schema = this.Parse(typeAlternative, parent, namedSchemas);
var schema = this.Parse(typeAlternative, parent);
if (schema.Type == AvroType.Union)
{
throw new SerializationException(
Expand All @@ -215,12 +201,11 @@ private TypeSchema ParseUnionType(JArray unionToken, NamedSchema parent, Diction
/// </summary>
/// <param name="enumeration">The JSON token that represents the enumeration.</param>
/// <param name="parent">The parent schema.</param>
/// <param name="namedSchemas">The named schemas.</param>
/// <returns>
/// Instance of <see cref="TypeSchema" /> containing IR of the enumeration.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when <paramref name="enumeration"/> contains invalid symbols.</exception>
private TypeSchema ParseEnumType(JObject enumeration, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas)
private TypeSchema ParseEnumType(JObject enumeration, NamedSchema parent)
{
var name = enumeration.RequiredProperty<string>(AvroKeywords.Name);
var nspace = this.GetNamespace(enumeration, parent, name);
Expand All @@ -246,7 +231,6 @@ private TypeSchema ParseEnumType(JObject enumeration, NamedSchema parent, Dictio

//fixme: runtime type cannot be provided for json schema resolution. Providing general type
var result = new EnumSchema(attributes, typeof(Enum), customAttributes);
namedSchemas.Add(result.FullName, result);
symbols.ForEach(result.AddSymbol);
return result;
}
Expand All @@ -256,12 +240,11 @@ private TypeSchema ParseEnumType(JObject enumeration, NamedSchema parent, Dictio
/// </summary>
/// <param name="array">JSON representing the array.</param>
/// <param name="parent">The parent.</param>
/// <param name="namedSchemas">The named schemas.</param>
/// <returns>
/// A corresponding schema.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when no 'items' property is found in <paramref name="array" />.</exception>
private TypeSchema ParseArrayType(JObject array, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas)
private TypeSchema ParseArrayType(JObject array, NamedSchema parent)
{
var itemType = array[AvroKeywords.Items];
if (itemType == null)
Expand All @@ -270,7 +253,7 @@ private TypeSchema ParseArrayType(JObject array, NamedSchema parent, Dictionary<
string.Format(CultureInfo.InvariantCulture, "Property 'items' cannot be found inside the array '{0}'.", array));
}

var elementSchema = this.Parse(itemType, parent, namedSchemas);
var elementSchema = this.Parse(itemType, parent);
return new ArraySchema(elementSchema, typeof(Array));
}

Expand All @@ -279,12 +262,11 @@ private TypeSchema ParseArrayType(JObject array, NamedSchema parent, Dictionary<
/// </summary>
/// <param name="map">JSON representing the map.</param>
/// <param name="parent">The parent.</param>
/// <param name="namedSchemas">The named schemas.</param>
/// <returns>
/// A corresponding schema.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when 'values' property is not found in <paramref name="map" />.</exception>
private TypeSchema ParseMapType(JObject map, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas)
private TypeSchema ParseMapType(JObject map, NamedSchema parent)
{
var valueType = map[AvroKeywords.Values];
if (valueType == null)
Expand All @@ -293,11 +275,11 @@ private TypeSchema ParseMapType(JObject map, NamedSchema parent, Dictionary<stri
string.Format(CultureInfo.InvariantCulture, "Property 'values' cannot be found inside the map '{0}'.", map));
}

var valueSchema = this.Parse(valueType, parent, namedSchemas);
var valueSchema = this.Parse(valueType, parent);
return new MapSchema(new StringSchema(), valueSchema, typeof(Dictionary<string, object>));
}

private TypeSchema ParseLogicalType(JObject token, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas, string logicalType)
private TypeSchema ParseLogicalType(JObject token, NamedSchema parent, string logicalType)
{
TypeSchema result;
switch (logicalType)
Expand Down Expand Up @@ -341,12 +323,11 @@ private TypeSchema ParseLogicalType(JObject token, NamedSchema parent, Dictionar
/// </summary>
/// <param name="record">The record.</param>
/// <param name="parent">The parent schema.</param>
/// <param name="namedSchemas">The named schemas.</param>
/// <returns>
/// Schema internal representation.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when <paramref name="record"/> can not be parsed properly.</exception>
private TypeSchema ParseRecordType(JObject record, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas)
private TypeSchema ParseRecordType(JObject record, NamedSchema parent)
{
var name = record.RequiredProperty<string>(AvroKeywords.Name);
var nspace = this.GetNamespace(record, parent, name);
Expand All @@ -358,11 +339,7 @@ private TypeSchema ParseRecordType(JObject record, NamedSchema parent, Dictionar

Dictionary<string, string> customAttributes = record.GetAttributesNotIn(StandardProperties.Record);
var result = new RecordSchema(attributes, typeof(object), customAttributes);
if (namedSchemas.ContainsKey(result.FullName))
{
return namedSchemas[result.FullName];
}
namedSchemas.Add(result.FullName, result);


List<RecordFieldSchema> fields = record.OptionalArrayProperty(
AvroKeywords.Fields,
Expand All @@ -373,7 +350,7 @@ private TypeSchema ParseRecordType(JObject record, NamedSchema parent, Dictionar
throw new SerializationException(
string.Format(CultureInfo.InvariantCulture, "Property 'fields' has invalid value '{0}'.", field));
}
return this.ParseRecordField(field as JObject, result, namedSchemas, index);
return this.ParseRecordField(field as JObject, result, index);
});

fields.ForEach(result.AddField);
Expand All @@ -391,7 +368,7 @@ private TypeSchema ParseRecordType(JObject record, NamedSchema parent, Dictionar
/// Schema internal representation.
/// </returns>
/// <exception cref="System.Runtime.Serialization.SerializationException">Thrown when <paramref name="field"/> is not valid or when sort order is not valid.</exception>
private RecordFieldSchema ParseRecordField(JObject field, NamedSchema parent, Dictionary<string, NamedSchema> namedSchemas, int position)
private RecordFieldSchema ParseRecordField(JObject field, NamedSchema parent, int position)
{
var name = field.RequiredProperty<string>(AvroKeywords.Name);
var doc = field.OptionalProperty<string>(AvroKeywords.Doc);
Expand All @@ -404,7 +381,7 @@ private RecordFieldSchema ParseRecordField(JObject field, NamedSchema parent, Di
string.Format(CultureInfo.InvariantCulture, "Record field schema '{0}' has no type.", field));
}

TypeSchema type = this.Parse(fieldType, parent, namedSchemas);
TypeSchema type = this.Parse(fieldType, parent);
object defaultValue = null;
bool hasDefaultValue = field[AvroKeywords.Default] != null;
if (hasDefaultValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ internal override bool CanRead(TypeSchema writerSchema)
return writerSchema.Type == Type || writerSchema.Type == BaseTypeSchema.Type;
}

internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
writer.WriteStartObject();
writer.WritePropertyName("type");
BaseTypeSchema.ToJsonSafe(writer, seenSchemas);
BaseTypeSchema.ToJsonSafe(writer);
writer.WriteProperty("logicalType", LogicalTypeName);
writer.WriteEndObject();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected PrimitiveTypeSchema(Type runtimeType, Dictionary<string, string> attri
{
}

internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
writer.WriteValue(CultureInfo.InvariantCulture.TextInfo.ToLower(this.Type.ToString()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected TypeSchema(Type runtimeType, IDictionary<string, string> attributes) :

private string emptySchema = "{\"name\":\"Object\",\"namespace\":\"System\",\"type\":\"record\",\"fields\":[]}";

internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
writer.WriteValue(CultureInfo.InvariantCulture.TextInfo.ToLower(this.Type.ToString()));
}
Expand Down
4 changes: 2 additions & 2 deletions src/AvroConvert/AvroObjectServices/Schemas/ArraySchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ internal TypeSchema ItemSchema
/// </summary>
/// <param name="writer">The writer.</param>
/// <param name="seenSchemas">The seen schemas.</param>
internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
writer.WriteStartObject();
writer.WriteProperty("type", "array");
writer.WritePropertyName("items");
this.itemSchema.ToJson(writer, seenSchemas);
this.itemSchema.ToJson(writer);
writer.WriteEndObject();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public DecimalSchema(Type runtimeType, int precision, int scale) : base(runtimeT
Precision = precision;
}

internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
writer.WriteStartObject();
writer.WriteProperty("type", BaseTypeSchema.Type.ToString().ToLowerInvariant());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public DurationSchema(Type runtimeType) : base(runtimeType)
internal override TypeSchema BaseTypeSchema { get; set; }
internal override string LogicalTypeName => "duration";

internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
var baseSchema = (FixedSchema)BaseTypeSchema;
writer.WriteStartObject();
Expand Down
9 changes: 1 addition & 8 deletions src/AvroConvert/AvroObjectServices/Schemas/EnumSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,8 @@ internal string GetSymbolByValue(int value)

internal long[] AvroToCSharpValueMapping => this.avroToCSharpValueMapping.ToArray();

internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
if (seenSchemas.Contains(this))
{
writer.WriteValue(this.FullName);
return;
}

seenSchemas.Add(this);
writer.WriteStartObject();
writer.WriteProperty("type", "enum");
writer.WriteProperty("name", Name);
Expand Down
9 changes: 1 addition & 8 deletions src/AvroConvert/AvroObjectServices/Schemas/FixedSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,8 @@ internal FixedSchema(NamedEntityAttributes namedEntityAttributes, int size, Type

internal int Size { get; }

internal override void ToJsonSafe(JsonTextWriter writer, HashSet<NamedSchema> seenSchemas)
internal override void ToJsonSafe(JsonTextWriter writer)
{
if (seenSchemas.Contains(this))
{
writer.WriteValue(this.FullName);
return;
}

seenSchemas.Add(this);
writer.WriteStartObject();
writer.WriteProperty("type", "fixed");
writer.WriteProperty("name", Name);
Expand Down

0 comments on commit 17b6146

Please sign in to comment.