Search code examples
c#avro

Avro C# generate classes using types defined across multiple schema files


I'm using the Apache.Avro package to generate some C# from some Avro schemas. I have separate schemas that share common building blocks, like Enum definitions.

I'm having an issue where references to common shared schemas don't resolve properly.

Here's the model, defined in one file:

{
    "name": "MyModel",
    "namespace": "My.Model",
    "type": "record",
    "fields": [
        {
            "name": "Planet",
            "type": "My.Model.PlanetEnum",
        }
    ]
}

Here's the definition for the My.Model.PlanetEnum, which is to be shared across multiple models:


{
  "name": "PlanetEnum",
  "namespace": "My.Model",
  "type": "enum",
  "symbols": [
    "Earth",
    "Mars"
  ]
}

Here's the C# I'm using to generate the schemas:

        var codeCompileUnit = new CodeGen
        {
            Schemas =
            {
/* generation fails even if I include the enum definition inline here
                Schema.Parse("""
{
  "name": "PlanetEnum",
  "namespace": "My.Model",
  "type": "enum",
  "symbols": [
    "Earth",
    "Mars"
  ]
}
"""),
*/
                Schema.Parse(schemaText)
            },
        }.GenerateCode();

        var stringWriter = new StringWriter(new StringBuilder());
        var provider = CodeDomProvider.CreateProvider("csharp");
        provider.GenerateCodeFromCompileUnit(codeCompileUnit, stringWriter, new CodeGeneratorOptions());
        var schemaCode = stringWriter.ToString();
        return schemaCode;

The error I get is:

Avro.SchemaParseException: Undefined name: My.Model.PlanetEnum at 'fields[0].type'
   at Avro.Schema.ParseJson(JToken jtok, SchemaNames names, String encspace)
   at Avro.RecordSchema.createField(JToken jfield, Int32 pos, SchemaNames names, String encspace)
   at Avro.RecordSchema.NewInstance(Type type, JToken jtok, PropertyMap props, SchemaNames names, String encspace)
   at Avro.NamedSchema.NewInstance(JObject jo, PropertyMap props, SchemaNames names, String encspace)
   at Avro.Schema.ParseJson(JToken jtok, SchemaNames names, String encspace)
   at Avro.Schema.Parse(String json, SchemaNames names, String encspace)
   at Avro.Schema.Parse(String json)

Solution

  • I managed to generate the C# code by combining the two schemas like this:

        var combinedSchemaText = $"[{planetEnumSchemaText},{schemaText}]";
        var combinedSchema = Schema.Parse(combinedSchemaText);
    

    The complete code that I used:

    
            public static void Main()
            {
                var schemaText = @"
                {
                    ""name"": ""MyModel"",
                    ""namespace"": ""My.Model"",
                    ""type"": ""record"",
                    ""fields"": [
                        {
                            ""name"": ""Planet"",
                            ""type"": ""My.Model.PlanetEnum""
                        }
                    ]
                }";
    
                var code = GenerateCSharpCodeCombined(schemaText);
                Console.WriteLine("Generated C# code:");
                Console.WriteLine(code);
            }
    
            private static string GenerateCSharpCodeCombined(string schemaText)
            {
                var planetEnumSchemaText = @"
                {
                    ""name"": ""PlanetEnum"",
                    ""namespace"": ""My.Model"",
                    ""type"": ""enum"",
                    ""symbols"": [
                    ""Earth"",
                    ""Mars""
                    ]
                }";
    
                var combinedSchemaText = $"[{planetEnumSchemaText},{schemaText}]";
                var combinedSchema = Schema.Parse(combinedSchemaText);
    
                var codeGen = new CodeGen();
                codeGen.Schemas.Add(combinedSchema);
    
                var codeCompileUnit = codeGen.GenerateCode();
    
                var stringWriter = new StringWriter(new StringBuilder());
                var provider = CodeDomProvider.CreateProvider("csharp");
                provider.GenerateCodeFromCompileUnit(codeCompileUnit, stringWriter, new CodeGeneratorOptions());
                var schemaCode = stringWriter.ToString();
    
                return schemaCode;
            }
    
    

    Check it and let's see if this solves your problem.