You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@avro.apache.org by "Dave Kliczbor (Jira)" <ji...@apache.org> on 2024/04/04 12:53:00 UTC
[jira] [Updated] (AVRO-3969) [C#] Serializing a Map/Dictionary using SpecificDefaultWriter and JsonEncoder fails with AvroTypeException
[ https://issues.apache.org/jira/browse/AVRO-3969?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Dave Kliczbor updated AVRO-3969:
--------------------------------
Language: C#
Labels: easyfix (was: )
> [C#] Serializing a Map/Dictionary using SpecificDefaultWriter and JsonEncoder fails with AvroTypeException
> ----------------------------------------------------------------------------------------------------------
>
> Key: AVRO-3969
> URL: https://issues.apache.org/jira/browse/AVRO-3969
> Project: Apache Avro
> Issue Type: Bug
> Components: csharp
> Affects Versions: 1.11.3
> Environment: We believe the environment to not matter here, but as there is a box for that here... :)
> * Windows 11
> * .NET 8.0
> * C# 12
> * Apache.Avro 1.11.3 (released via nuget)
> Reporter: Dave Kliczbor
> Priority: Major
> Labels: easyfix
> Original Estimate: 4h
> Remaining Estimate: 4h
>
> Hello,
> We think we identified a bug in the SpecificDefaultWriter implementation of the Apache.Avro C# library (as of Release 1.11.3, also main branch as of 2024-04-03).
> h4. Reproduction of the problem:
> The following C# code throws an {{{}AvroTypeException("Attempt to process an array-start when a map-start was expected"){}}}:
> {code:java}
> var schema = MapSchema.CreateMap(PrimitiveSchema.Create(Schema.Type.String));
> var ms = new MemoryStream();
> Encoder enc = new JsonEncoder(schema, ms, false);
> var writer = new SpecificDefaultWriter(schema);
> writer.Write(new Dictionary<string, string>(), enc);
> enc.Flush();{code}
>
> We expect this code to not throw up.
> The same code with {{BinaryEncoder(schema, ms)}} instead of {{JsonEncoder(schema, ms, false)}} works as expected: we get a Avro message byte array without an Exception being thrown.
> h4. Preliminary Solution Analysis:
> We believe the root cause to be in this 13 years old code:
> [https://github.com/apache/avro/blob/8cb32f05d50382e5a5e97d6d45b080bef0c0e6a5/lang/csharp/src/apache/main/Specific/SpecificWriter.cs#L152]
> {code:java}
> protected override void WriteMap(MapSchema schema, object value, Encoder encoder)
> {
> var map = value as System.Collections.IDictionary;
> if (map == null)
> throw new AvroTypeException("Map does not implement non-generic IDictionary");
> encoder.WriteArrayStart(); // <----- This is L.152
> encoder.SetItemCount(map.Count);
> foreach (System.Collections.DictionaryEntry de in map)
> {
> encoder.StartItem();
> encoder.WriteString(de.Key as string);
> Write(schema.ValueSchema, de.Value, encoder);
> }
> encoder.WriteMapEnd();
> } {code}
> In method {{{}SpecificDefaultWriter.WriteMap(MapSchema, object, Encoder){}}}, the call {{encoder.WriteArrayStart()}} is used. Which is in stark contrast to the usage of {{encoder.WriteMapEnd()}} later and also matches the Exception message above (using the BinaryEncoder works without problems, as {{WriteArrayStart}} and {{WriteMapStart }}are identical empty methods there).
> Therefore, we believe that {{encoder.WriteMapStart();}} is the intended call in L.152.
>
> h4. Additional information:
> Here’s our test case (code using xUnit/FluentAssertions and Apache.Avro 1.11.3) that fails with the bug present:
>
> {code:java}
> [Fact]
> public void AvroLibraryJsonEncoderDoesNotChokeOnMaps()
> {
> Action action = () =>
> {
> var schema = MapSchema.CreateMap(PrimitiveSchema.Create(Schema.Type.String));
> var ms = new MemoryStream();
> Encoder enc = new JsonEncoder(schema, ms, false);
> var writer = new SpecificDefaultWriter(schema);
> writer.Write(new Dictionary<string, string>(), enc);
> enc.Flush();
> };
> action.Should().NotThrow<AvroTypeException>();
> }{code}
>
> Our workaround is to use this derived SpecificDefaultWriter class instead of the original from the Apache.Avro C# library. This works for our purposes, but up to now, we are mere users of the library and do not know yet whether this might break something else.
> {code:java}
> public class SpecificDefaultWriterWithWriteMapToJsonBugfix(Schema schema) : SpecificDefaultWriter(schema) {
> protected override void WriteMap(MapSchema schema, object value, Encoder encoder)
> {
> if (!(value is IDictionary dictionary))
> throw new AvroTypeException("Map does not implement non-generic IDictionary");
> encoder.WriteMapStart();
> encoder.SetItemCount((long)dictionary.Count);
> foreach (DictionaryEntry dictionaryEntry in dictionary)
> {
> encoder.StartItem();
> encoder.WriteString(dictionaryEntry.Key as string);
> this.Write(schema.ValueSchema, dictionaryEntry.Value, encoder);
> }
> encoder.WriteMapEnd();
> }
> }
> {code}
> Cheers!
> Dave Kliczbor (Materna SE)
--
This message was sent by Atlassian Jira
(v8.20.10#820010)