Closed koncq closed 1 year ago
For the first issue, could you provide the trace as well as the TestClassRoot
class if you can?
For the second, it should work to create custom cases for T
. See https://github.com/ch-robinson/dotnet-avro/blob/main/examples/Chr.Avro.UnionTypeExample/Program.cs for an example. If you want to use int
as the dictionary key, you may also have to implement a custom case for int
since Avro map keys are strings and Chr.Avro won’t convert int
to string
implicitly.
Trace:
Chr.Avro.UnsupportedTypeException: The RequestInfoContext member on DataContract.EngineRequest could not be mapped to the RequestInfoContext field on DataContract.EngineRequest.
---> Chr.Avro.UnsupportedTypeException: The Parameter member on DataContract.Data.InfoContext could not be mapped to the Parameter field on DataContract.Data.InfoContext.
---> Chr.Avro.UnsupportedTypeException: Failed to map StringSchema to System.Char[].
---> System.InvalidOperationException: No coercion operator is defined between types 'System.String' and 'System.Char[]'.
at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType)
at Chr.Avro.Serialization.ExpressionBuilder.BuildStaticConversion(Expression value, Type target)
at Chr.Avro.Serialization.StringDeserializerBuilderCase.BuildStaticConversion(Expression value, Type target)
at Chr.Avro.Serialization.ExpressionBuilder.BuildConversion(Expression value, Type target)
at Chr.Avro.Serialization.BinaryStringDeserializerBuilderCase.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
--- End of inner exception stack trace ---
at Chr.Avro.Serialization.BinaryStringDeserializerBuilderCase.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryDeserializerBuilder.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.<>c__DisplayClass7_1.<BuildExpression>b__0(RecordField field)
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryDeserializerBuilder.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.<>c__DisplayClass7_1.<BuildExpression>b__0(RecordField field)
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryDeserializerBuilder.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.<>c__DisplayClass7_3.<BuildExpression>b__7(RecordField field)
--- End of inner exception stack trace ---
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.<>c__DisplayClass7_3.<BuildExpression>b__7(RecordField field)
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
at System.Collections.Generic.SparseArrayBuilder`1.ReserveOrAdd(IEnumerable`1 items)
at System.Linq.Enumerable.ConcatNIterator`1.LazyToArray()
at System.Dynamic.Utils.CollectionExtensions.ToReadOnly[T](IEnumerable`1 enumerable)
at System.Linq.Expressions.Expression.Block(IEnumerable`1 variables, IEnumerable`1 expressions)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryDeserializerBuilder.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.<>c__DisplayClass7_3.<BuildExpression>b__7(RecordField field)
--- End of inner exception stack trace ---
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.<>c__DisplayClass7_3.<BuildExpression>b__7(RecordField field)
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
at System.Collections.Generic.SparseArrayBuilder`1.ReserveOrAdd(IEnumerable`1 items)
at System.Linq.Enumerable.ConcatNIterator`1.LazyToArray()
at System.Dynamic.Utils.CollectionExtensions.ToReadOnly[T](IEnumerable`1 enumerable)
at System.Linq.Expressions.Expression.Block(IEnumerable`1 variables, IEnumerable`1 expressions)
at Chr.Avro.Serialization.BinaryRecordDeserializerBuilderCase.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryDeserializerBuilder.BuildExpression(Type type, Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryDeserializerBuilder.BuildDelegateExpression[T](Schema schema, BinaryDeserializerBuilderContext context)
at Chr.Avro.Serialization.BinaryDeserializerBuilder.BuildDelegate[T](Schema schema, BinaryDeserializerBuilderContext context)
at KafkaMessageConsumer.Deserialize(Byte[] data, Schema schema) in C:\Repos\AvroGen\KafkaMessageConsumer.cs:line 29
at KafkaMessageConsumer.Consume(ConsumeContext`1 context) in C:\Repos\AvroGen\KafkaMessageConsumer.cs:line 20
Simplified class, without multiple, additional properties, stripped of ProtoMemberAttributes:
[DataContract]
public sealed class Request
{
[DataMember] public bool Debug { get; set; }
[DataMember] public int NetworkId { get; set; } = -1;
[DataMember] public int SiteId { get; set; } = -1;
[DataMember] public int PageId { get; set; }
[DataMember] public IEnumerable<Size> RequestedSizes { get; set; }
[DataMember] public string RequestUrl { get; set; }
[DataMember] public string PageUrl { get; set; }
[DataMember] public string PageDomain { get; set; }
[DataMember] public string Keywords { get; set; }
[DataMember] public InfoContext RequestInfoContext { get; set; }
[DataMember] public bool HasIfa { get; set; }
[DataMember] public bool HasIfv { get; set; }
[DataMember] public string ImpressionUrl { get; set; }
[DataMember] public string MraidOnePxImpressionUrl { get; set; }
[DataMember] public string InventoryPartnerDomain { get; set; }
[DataMember] public string UniqueId { get; set; }
}
[DataContract]
public class InfoContext
{
[DataMember] public TestClass Parameter { get; set; }
[DataMember] public int MaxDuration { get; set; }
[DataMember] public int Size { get; set; }
}
[DataContract]
public class TestClass
{
[DataMember] public string Platform { get; private set; }
[DataMember] public int CurrentSpot { get; private set; }
[DataMember] public string AdvertisingId { get; private set; }
[DataMember] public string IdType { get; private set; }
[DataMember] public bool IsLat { get; private set; }
[DataMember] public string TransactionId { get; private set; }
[DataMember] public TvDaiFeature DaiFeature { get; private set; }
}
Eveery other property gets mapped, except for that IdType
in TestClass
.
I would like to use int
as the key, I did not write it clear enough, my bad. Value Types are easy, it is basically handled automatically by SchemaBuilder. So what would I have to do to have int
keys?
It looks like private set
on those properties might be the issue here. To get around that, try adding BindingFlags.NonPublic
to memberVisibility
.
Custom serde cases would be the way to handle value type conversions as well: implement a case that matches the "int"
schema and the string
type.
Well, cannot really go with BindingFlags.NonPublic
, but when I set a public setter, it did not really help...
Hmm, I'm unable to reproduce with the following code on .NET 7:
var schemaBuilder = new SchemaBuilder(
memberVisibility: BindingFlags.Public | BindingFlags.Instance,
enumBehavior: EnumBehavior.Symbolic,
nullableReferenceTypeBehavior: NullableReferenceTypeBehavior.Annotated,
temporalBehavior: TemporalBehavior.Iso8601);
var schemaBuilderContext = new SchemaBuilderContext();
var schema = schemaBuilder.BuildSchema<Request>(schemaBuilderContext);
var deserializer = new BinaryDeserializerBuilder(
BinaryDeserializerBuilder
.CreateDefaultCaseBuilders()
)
.BuildDelegate<Request>(schema);
Which version of .NET and which version of Chr.Avro are you using?
.net 7 and 9.4.1 - latest. i am unable to provide the whole project with repro today. I assume it won't be so easy to reproduce it without the whole context...
I have sent you link to the repo with reproduced issue.
EDIT: Could you explain what did you mean by?
Custom serde cases would be the way to handle value type conversions as well: implement a case that matches the "int" schema and the string type
When I run the reproduction locally, this is the trace I see:
Creating random SSPEngineRequest
Serializing request...
Deserializing request...
Unhandled exception. Chr.Avro.UnsupportedTypeException: The RequestContext member on AvroRepro.Request could not be mapped to the RequestContext field on AvroRepro.Request.
---> Chr.Avro.UnsupportedTypeException: The Parameter member on AvroRepro.Context could not be mapped to the Parameter field on AvroRepro.Context.
---> System.ArgumentException: Expression of type 'System.Object' cannot be used for constructor parameter of type 'System.String' (Parameter 'arguments[14]')
at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
If I remove the spotId
parameter on the Parameter
constructor, the program completes successfully. There’s no corresponding SpotId
property on the Parameter
class.
Custom serde cases would be the way to handle value type conversions as well: implement a case that matches the "int" schema and the string type
I’ll try to be a little more detailed. The easiest path would probably be to create a class that extends BinaryStringSerializerBuilderCase
and override BuildStaticConversion
to support conversion from int
.
Hi, this is really a question regarding 2 seperate issues, but I did not want to create 2 issues.
The first thing, it looks like a bug to me. So I have a following class (which is nested in the main class
TestClassRoot
):During deserialization process I am getting following error message:
No coercion operator is defined between types 'System.String' and 'System.Char[]'.
Which, to me, is kinda strange, when I have couple dozen of similar properties and they deserialize correctly. I am using following code to build schema:
Also, deserializer are basic, deserializer for reference:
The other issue is related to the serializing and deserializing C#'s
Dictionary<int, T>
. How can I embed a rule inSchemaBuilder
to support that kind of conversion? Or should I create a custom deserializer? As a workaround I create another property with supported type (Dictionary<string, string>
) and do parsing under the hood, which is not really sustainable...