RicoSuter / NJsonSchema

JSON Schema reader, generator and validator for .NET
http://NJsonSchema.org
MIT License
1.4k stars 535 forks source link

How can I remove duplicated Classes? #1232

Open diegoeche opened 4 years ago

diegoeche commented 4 years ago

Hello RicoSuter! I'm liking to use your library. I'm dealing with a problem trying to merge a bunch of calls into a big Schema that avoids duplication of classes that are pointing to the same object.

Here is my Scenario:

I have a bunch of endpoints defined as JSON Schemas in different directories:

\callOne\v1.json
\callTwo\v1.json
\callThree\v1.json

I generate code for all and since they reuse some entities, I end up with definitions that are duplicated, something like:

namespace CallOneNamespace {
  class CallOne {
     public Metadata { get; set; }
  }
  class Metadata {
  }
}
namespace CallTwoNamespace {
  class CallTwo {
    public Metadata { get; set; }
  }
  class Metadata {
  }
}

I tried merging all JSONSchema from different files into a bigger JSONSchema: In pseudocode I do something like:

       // (...)
                var metaSchema = new JsonSchema();
                foreach(var file in files) {
                    var schema = generator.LoadSchema(file);
                    metaSchema = generator.AddToMeta(metaSchema, schema, generator.ExtractDirectoryName(file));
                }
       // (...)
        // Where AddToMeta does this:
        public JsonSchema AddToMeta(JsonSchema parent, JsonSchema child, string name) {
            foreach(var definition in child.Definitions) {
                if(!parent.Definitions.ContainsKey(definition.Key)) {
                    parent.Definitions.Add(definition.Key, definition.Value);
                } else {
                    // Console.WriteLine(definition.Key);
                }
            }

            child.Definitions.Clear();
            parent.Definitions.Add(name, child);
            parent.AnyOf.Add(child);
            return parent;
        }

Basically make a JsonSchema, Make a big metaSchema using AnyOf, Move definitions from children to the big JSONSchema

But the result, is still duplicated classes but with a number to make them different. Something like:

namespace MetaSchema {
  class CallOne {
     public Metadata { get; set; }
  }
  class Metadata {
  }
  class CallTwo {
    public Metadata2 { get; set; }
  }
  class Metadata2 {
  }
}

I read an issue similar to this: https://github.com/RicoSuter/NJsonSchema/issues/646

I tried to filter the duplicated types, but then the references still have the old type name. Something like:

namespace MetaSchema {
  class CallOne {
     public Metadata { get; set; }
  }
  class Metadata {
  }
  class CallTwo {
    public Metadata2 { get; set; } // This type got deleted from the IEnumerable<CodeArtifact>
  }
}

And as a 3rd approach, I tried instead of merging a big JsonSchema, I tried calling "GenerateTypes" as you suggested in the above issue. Something like:

                var types = files
                    .Select((x) => generator.GenerateTypes(x))
                    .SelectMany((x) => x)
                    .Distinct(new DistinctCodeArtifactComparer());

But super weird, GenerateTypes is always returning an empty collection. It generates code, but it doesn't generate a list of Types.

        public IEnumerable<CodeArtifact> GenerateTypes(string contractPath) {
            var namespace_ = ExtractDirectoryName(contractPath);
            var schema = LoadSchema(contractPath);
            var sourcePath = "./gen/" + namespace_ + ".cs";
            var schemaId = schema.ExtensionData["schemaId"];
            var settings = new CSharpGeneratorSettings();
            settings.Namespace = namespace_;
            settings.GenerateDataAnnotations = false;
            var types = new MyCSharpGenerator(schema, settings).GenerateTypes();
            // This outputs code!
            Console.WriteLine(new MyCSharpGenerator(schema, settings).GenerateFile());
            // This list is always empty!
            foreach(var type in types) {
                Console.WriteLine(type.TypeName);
            }
            return types;
        }

Some questions:

Is there an easier way to "Merge" all metadata classes from different JsonSchema files?

Why GenerateTypes isn't returning anything?

RicoSuter commented 4 years ago

Is there an easier way to "Merge" all metadata classes from different JsonSchema files

Currently not no, you need to do this manually which might be not that easy...

You would not only need to remove unused schemas but also change all references to the removed ones (maybe with a custom JsonSchemaVistior, see NJS code), ie change the JsonSchema.Reference property to point to the new/correct schema

Why GenerateTypes isn't returning anything

This should work, ie it should generate all types in root and "definitions"

CarvellWakeman commented 2 years ago

@RicoSuter Has there been any update adding this capability? Or @diegoeche did you come up with a good solution with the current set of tools?

I was thinking of going a similar route with a top level json schema and merging properties together, it's unfortunate to hear it didn't work out for you.

I've also only seen empty arrays coming from GenerateTypes, I don't want to push that issue though because I don't have repro steps and haven't had time to investigate further.

diegoeche commented 2 years ago

@CarvellWakeman Since my json-schema fields were actually simple, I ended up using a simple python script with jinja2 templates to generate the stub code, instead of using this library.