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.
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
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
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
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
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
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
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
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
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
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
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.