You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by bl...@apache.org on 2020/11/15 00:31:36 UTC
[avro] branch branch-1.10 updated: AVRO-2750: Add support for enum
defaults in c#
This is an automated email from the ASF dual-hosted git repository.
blachniet pushed a commit to branch branch-1.10
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/branch-1.10 by this push:
new aeef3bf AVRO-2750: Add support for enum defaults in c#
aeef3bf is described below
commit aeef3bf628a090abe5952cf2e858cec3d48004c4
Author: Matt Kellogg <ma...@gmail.com>
AuthorDate: Tue Feb 18 15:03:14 2020 -0700
AVRO-2750: Add support for enum defaults in c#
---
lang/csharp/src/apache/main/CodeGen/CodeGen.cs | 4 ++
lang/csharp/src/apache/main/Generic/GenericEnum.cs | 17 ++++++-
.../apache/main/Generic/PreresolvingDatumReader.cs | 9 +++-
lang/csharp/src/apache/main/Schema/EnumSchema.cs | 25 +++++++++--
lang/csharp/src/apache/test/CodGen/CodeGenTest.cs | 3 +-
.../csharp/src/apache/test/Generic/GenericTests.cs | 9 ++++
lang/csharp/src/apache/test/Schema/SchemaTests.cs | 14 ++++++
.../src/apache/test/Specific/SpecificTests.cs | 52 ++++++++++++++++++----
8 files changed, 117 insertions(+), 16 deletions(-)
diff --git a/lang/csharp/src/apache/main/CodeGen/CodeGen.cs b/lang/csharp/src/apache/main/CodeGen/CodeGen.cs
index 09abcab..70ab5bd 100644
--- a/lang/csharp/src/apache/main/CodeGen/CodeGen.cs
+++ b/lang/csharp/src/apache/main/CodeGen/CodeGen.cs
@@ -607,6 +607,10 @@ namespace Avro
string privFieldName = string.Concat("_", field.Name);
var codeField = new CodeMemberField(ctrfield, privFieldName);
codeField.Attributes = MemberAttributes.Private;
+ if (field.Schema is EnumSchema es && es.Default != null)
+ {
+ codeField.InitExpression = new CodeTypeReferenceExpression($"{es.Name}.{es.Default}");
+ }
// Process field documentation if it exist and add to the field
CodeCommentStatement propertyComment = null;
diff --git a/lang/csharp/src/apache/main/Generic/GenericEnum.cs b/lang/csharp/src/apache/main/Generic/GenericEnum.cs
index 00357f2..168b555 100644
--- a/lang/csharp/src/apache/main/Generic/GenericEnum.cs
+++ b/lang/csharp/src/apache/main/Generic/GenericEnum.cs
@@ -37,8 +37,21 @@ namespace Avro.Generic
get { return value; }
set
{
- if (! Schema.Contains(value)) throw new AvroException("Unknown value for enum: " + value + "(" + Schema + ")");
- this.value = value;
+ if (!Schema.Contains(value))
+ {
+ if (!string.IsNullOrEmpty(Schema.Default))
+ {
+ this.value = Schema.Default;
+ }
+ else
+ {
+ throw new AvroException("Unknown value for enum: " + value + "(" + Schema + ")");
+ }
+ }
+ else
+ {
+ this.value = value;
+ }
}
}
diff --git a/lang/csharp/src/apache/main/Generic/PreresolvingDatumReader.cs b/lang/csharp/src/apache/main/Generic/PreresolvingDatumReader.cs
index c55957a..a4b4aa8 100644
--- a/lang/csharp/src/apache/main/Generic/PreresolvingDatumReader.cs
+++ b/lang/csharp/src/apache/main/Generic/PreresolvingDatumReader.cs
@@ -195,8 +195,10 @@ namespace Avro.Generic
var translator = new int[writerSchema.Symbols.Count];
+ var readerDefaultOrdinal = null != readerSchema.Default ? readerSchema.Ordinal(readerSchema.Default) : -1;
+
foreach (var symbol in writerSchema.Symbols)
- {
+ {
var writerOrdinal = writerSchema.Ordinal(symbol);
if (readerSchema.Contains(symbol))
{
@@ -212,6 +214,11 @@ namespace Avro.Generic
{
var writerOrdinal = d.ReadEnum();
var readerOrdinal = translator[writerOrdinal];
+ if (readerOrdinal == -1 && readerDefaultOrdinal != -1) //the symbol doesn't exist, but the default does
+ {
+ return enumAccess.CreateEnum(r, readerDefaultOrdinal);
+ }
+
if (readerOrdinal == -1)
{
throw new AvroException("No such symbol: " + writerSchema[writerOrdinal]);
diff --git a/lang/csharp/src/apache/main/Schema/EnumSchema.cs b/lang/csharp/src/apache/main/Schema/EnumSchema.cs
index 5a14afd..3fd1450 100644
--- a/lang/csharp/src/apache/main/Schema/EnumSchema.cs
+++ b/lang/csharp/src/apache/main/Schema/EnumSchema.cs
@@ -33,6 +33,11 @@ namespace Avro
public IList<string> Symbols { get; private set; }
/// <summary>
+ /// The default token to use when deserializing an enum when the provided token is not found
+ /// </summary>
+ public string Default { get; private set; }
+
+ /// <summary>
/// Map of enum symbols and it's corresponding ordinal number
/// </summary>
private readonly IDictionary<string, int> symbolMap;
@@ -74,7 +79,7 @@ namespace Avro
try
{
return new EnumSchema(name, aliases, symbols, symbolMap, props, names,
- JsonHelper.GetOptionalString(jtok, "doc"));
+ JsonHelper.GetOptionalString(jtok, "doc"), JsonHelper.GetOptionalString(jtok, "default"));
}
catch (SchemaParseException e)
{
@@ -92,14 +97,19 @@ namespace Avro
/// <param name="props">custom properties on this schema</param>
/// <param name="names">list of named schema already read</param>
/// <param name="doc">documentation for this named schema</param>
+ /// <param name="defaultSymbol">default symbol</param>
private EnumSchema(SchemaName name, IList<SchemaName> aliases, List<string> symbols,
IDictionary<String, int> symbolMap, PropertyMap props, SchemaNames names,
- string doc)
+ string doc, string defaultSymbol)
: base(Type.Enumeration, name, aliases, props, names, doc)
{
if (null == name.Name) throw new SchemaParseException("name cannot be null for enum schema.");
this.Symbols = symbols;
this.symbolMap = symbolMap;
+
+ if (null != defaultSymbol && !symbolMap.ContainsKey(defaultSymbol))
+ throw new SchemaParseException($"Default symbol: {defaultSymbol} not found in symbols");
+ Default = defaultSymbol;
}
/// <summary>
@@ -117,6 +127,11 @@ namespace Avro
foreach (string s in this.Symbols)
writer.WriteValue(s);
writer.WriteEndArray();
+ if (null != Default)
+ {
+ writer.WritePropertyName("default");
+ writer.WriteValue(Default);
+ }
}
/// <summary>
@@ -128,7 +143,11 @@ namespace Avro
public int Ordinal(string symbol)
{
int result;
- if (symbolMap.TryGetValue(symbol, out result)) return result;
+ if (symbolMap.TryGetValue(symbol, out result))
+ return result;
+ if (null != Default)
+ return symbolMap[Default];
+
throw new AvroException("No such symbol: " + symbol);
}
diff --git a/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs b/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs
index 34358d6..c288989 100644
--- a/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs
+++ b/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs
@@ -18,7 +18,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using NUnit.Framework;
@@ -45,7 +44,7 @@ namespace Avro.Test
{ ""name"" : ""internal"", ""type"" : ""bytes"" },
{ ""name"" : ""while"", ""type"" : ""string"" },
{ ""name"" : ""return"", ""type"" : ""null"" },
- { ""name"" : ""enum"", ""type"" : { ""type"" : ""enum"", ""name"" : ""class"", ""symbols"" : [ ""A"", ""B"" ] } },
+ { ""name"" : ""enum"", ""type"" : { ""type"" : ""enum"", ""name"" : ""class"", ""symbols"" : [ ""Unknown"", ""A"", ""B"" ], ""default"" : ""Unknown"" } },
{ ""name"" : ""string"", ""type"" : { ""type"": ""fixed"", ""size"": 16, ""name"": ""static"" } }
]
}
diff --git a/lang/csharp/src/apache/test/Generic/GenericTests.cs b/lang/csharp/src/apache/test/Generic/GenericTests.cs
index f9817f9..05aa5bc 100644
--- a/lang/csharp/src/apache/test/Generic/GenericTests.cs
+++ b/lang/csharp/src/apache/test/Generic/GenericTests.cs
@@ -108,6 +108,15 @@ namespace Avro.Test.Generic
test(schema, mkMap(values));
}
+ [TestCase("{\"type\": \"enum\", \"name\": \"Test\", \"symbols\": [\"Unknown\", \"A\", \"B\"], \"default\": \"Unknown\" }", "C", "Unknown")]
+ [TestCase("{\"type\": \"enum\", \"name\": \"Test\", \"symbols\": [\"Unknown\", \"A\", \"B\"], \"default\": \"Unknown\" }", "A", "A")]
+ public void TestEnumDefault(string schema, string attemptedValue, string expectedValue)
+ {
+ var newEnum = mkEnum(schema, attemptedValue) as GenericEnum;
+ Assert.NotNull(newEnum);
+ Assert.AreEqual(newEnum.Value, expectedValue);
+ }
+
[TestCase()]
public void TestLogical_Date()
{
diff --git a/lang/csharp/src/apache/test/Schema/SchemaTests.cs b/lang/csharp/src/apache/test/Schema/SchemaTests.cs
index 50d54b7..1d67742 100644
--- a/lang/csharp/src/apache/test/Schema/SchemaTests.cs
+++ b/lang/csharp/src/apache/test/Schema/SchemaTests.cs
@@ -249,6 +249,20 @@ namespace Avro.Test
Assert.AreEqual(expectedDoc, es.Documentation);
}
+ [TestCase("{\"type\": \"enum\", \"name\": \"Test\", \"symbols\": [\"Unknown\", \"A\", \"B\"], \"default\": \"Unknown\" }", "Unknown")]
+ public void TestEnumDefault(string s, string expectedToken)
+ {
+ var es = Schema.Parse(s) as EnumSchema;
+ Assert.IsNotNull(es);
+ Assert.AreEqual(es.Default, expectedToken);
+ }
+
+ [TestCase("{\"type\": \"enum\", \"name\": \"Test\", \"symbols\": [\"Unknown\", \"A\", \"B\"], \"default\": \"Something\" }")]
+ public void TestEnumDefaultSymbolDoesntExist(string s)
+ {
+ Assert.Throws<SchemaParseException>(() => Schema.Parse(s));
+ }
+
[TestCase("{\"type\": \"array\", \"items\": \"long\"}", "long")]
public void TestArray(string s, string item)
{
diff --git a/lang/csharp/src/apache/test/Specific/SpecificTests.cs b/lang/csharp/src/apache/test/Specific/SpecificTests.cs
index 2602f6c..9756b2d 100644
--- a/lang/csharp/src/apache/test/Specific/SpecificTests.cs
+++ b/lang/csharp/src/apache/test/Specific/SpecificTests.cs
@@ -18,7 +18,6 @@
using System;
using System.Collections;
using System.IO;
-using System.Linq;
using NUnit.Framework;
using Avro.IO;
using System.CodeDom;
@@ -249,6 +248,24 @@ namespace Avro.Test
}
[Test]
+ public void TestEnumDefault()
+ {
+ //writerSchema has "SECOND"
+ Schema writerSchema = Schema.Parse("{ \"type\": \"record\", \"name\": \"EnumRecord\", \"fields\": [ { \"name\": \"enumType\", \"type\": { \"type\": \"enum\", \"name\": \"EnumType\", \"symbols\": [ \"DEFAULT\", \"FIRST\", \"SECOND\", \"THIRD\" ], \"default\": \"DEFAULT\" } } ] }");
+ Schema readerSchema = Schema.Parse("{ \"type\": \"record\", \"name\": \"EnumRecord\", \"fields\": [ { \"name\": \"enumType\", \"type\": { \"type\": \"enum\", \"name\": \"EnumType\", \"symbols\": [ \"DEFAULT\", \"FIRST\", \"THIRD\" ], \"default\": \"DEFAULT\" } } ] }");
+
+ //readerSchema is missing "SECOND" so should therefore be "DEFAULT"
+ var testRecord = new EnumRecord {enumType = EnumType.SECOND};
+
+ // serialize
+ var stream = serialize(writerSchema, testRecord);
+
+ // deserialize
+ var rec2 = deserialize<EnumRecord>(stream, writerSchema, readerSchema);
+ Assert.AreEqual(EnumType.DEFAULT, rec2.enumType);
+ }
+
+ [Test]
public void TestEmbeddedGenerics()
{
var srcRecord = new EmbeddedGenericsRecord
@@ -528,11 +545,12 @@ namespace Avro.Test
}
}
- enum EnumType
+ public enum EnumType
{
- THIRD,
+ DEFAULT, //putting the default first here so there isn't an ordinal collision for testing defaults
FIRST,
- SECOND
+ SECOND,
+ THIRD,
}
class EnumRecord : ISpecificRecord
@@ -541,10 +559,28 @@ namespace Avro.Test
public Schema Schema
{
get
- {
- return Schema.Parse("{\"type\":\"record\",\"name\":\"EnumRecord\",\"namespace\":\"Avro.Test\"," +
- "\"fields\":[{\"name\":\"enumType\",\"type\": { \"type\": \"enum\", \"name\":" +
- " \"EnumType\", \"symbols\": [\"THIRD\", \"FIRST\", \"SECOND\"]} }]}");
+ {
+ return Schema.Parse(@"{
+ ""type"":""record"",
+ ""name"":""EnumRecord"",
+ ""namespace"":""Avro.Test"",
+ ""fields"":[
+ {
+ ""name"":""enumType"",
+ ""type"":{
+ ""type"":""enum"",
+ ""name"":""EnumType"",
+ ""symbols"":[
+ ""DEFAULT"",
+ ""FIRST"",
+ ""SECOND"",
+ ""THIRD""
+ ]
+ },
+ ""default"": ""DEFAULT""
+ }
+ ]
+}");
}
}