NJsonSchema icon indicating copy to clipboard operation
NJsonSchema copied to clipboard

How do I set the name of a property for the C# generator via a visitor?

Open VictorioBerra opened this issue 6 years ago • 5 comments

My swagger looks like this

  "OptionChainPair": {
    "type": "object",
    "properties": {
      "optioncall": {
        "xml": {
          "name": "Call"
        },
        "description": "The option call in the option chain pair",
        "$ref": "#/definitions/OptionDetails"
      },
      "optionPut": {
        "xml": {
          "name": "Put"
        },
        "description": "The option put in the option chain pair",
        "$ref": "#/definitions/OptionDetails"
      },
      "pairType": {
        "type": "string",
        "description": "Determines whether the response will contain calls(CALLONLY), puts(PUTONLY), or both(CALLPUT)"
      }
    },
    "xml": {
      "name": "OptionPair"
    }
  }

Via https://apisb.etrade.com/docs/api/market/optionsjson/swagger.json

Any time I have an "xml": "name": "WhateverHere" I need to use THAT for the key.

For example, the above JSON should look like this when generated to a C# model:

/// <summary>Container for an option pair; each option pair in the response has a container</summary>
[Newtonsoft.Json.JsonProperty("OptionPair", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.Collections.Generic.List<OptionChainPair> OptionPairs { get; set; }

Not like this:

/// <summary>Container for an option pair; each option pair in the response has a container</summary>
[Newtonsoft.Json.JsonProperty("optionPairs", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.Collections.Generic.List<OptionChainPair> OptionPairs { get; set; }

I have some code like this:

    public class MyVisitor : JsonSchemaVisitorBase
    {
        protected override Task<JsonSchema4> VisitSchemaAsync(JsonSchema4 schema, string path, string typeNameHint)
        {		
            if (schema is SwaggerParameter || schema is JsonProperty)
            {
                if (schema.Type == JsonObjectType.Object)
                {
                    var schemaPropert = ((JsonProperty)schema);
                    schemaPropert.Name = schemaPropert.Xml.Name; // COMPILE ERROR: READONLY

                    foreach(var prop in schema.Properties)
                    {
                        prop.Value.Name = prop.Value.Xml.Name; // COMPILE ERROR: READONLY
                    }
                    }
                }
            }
            return Task.FromResult(schema);
        }
    }

The code wont compile and I am not sure how to tweak those keys.

VictorioBerra avatar Feb 12 '19 17:02 VictorioBerra

I use the following (not using a visitor):

    public static class ConversionHelper
    {
        public static string ConvertToUpperCamelCase(string input, bool firstCharacterMustBeAlpha)
        {
            if (String.IsNullOrEmpty(input))
            {
                return String.Empty;
            }
            input = input.Replace(" ", "_").Replace("/", "_");
            input = ConvertDashesToCamelCase(input[0].ToString().ToUpperInvariant() + input.Substring(1));
            if (String.IsNullOrEmpty(input))
            {
                return String.Empty;
            }
            if (firstCharacterMustBeAlpha && char.IsNumber(input[0]))
            {
                return "_" + input;
            }
            return input;
        }

        private static string ConvertDashesToCamelCase(string input)
        {
            StringBuilder stringBuilder = new StringBuilder();
            bool flag = false;
            foreach (char c in input)
            {
                if (c == '-' || c == '_')
                {
                    flag = true;
                }
                else if (flag)
                {
                    stringBuilder.Append(char.ToUpperInvariant(c));
                    flag = false;
                }
                else
                {
                    stringBuilder.Append(c);
                }
            }
            return stringBuilder.ToString();
        }
    }

    public class MyPropertyNameGenerator : IPropertyNameGenerator
    {
        string IPropertyNameGenerator.Generate(JsonProperty property)
        {
            return ConversionHelper.ConvertToUpperCamelCase(property.Name, true);
        }
    }

    public class MyTypeNameGenerator : DefaultTypeNameGenerator
    {
        protected override string Generate(JsonSchema4 schema, string typeNameHint)
        {
            if (string.IsNullOrEmpty(typeNameHint) && !string.IsNullOrEmpty(schema.Title) && Regex.IsMatch(schema.Title, "^[a-zA-Z0-9_]*$"))
            {
                typeNameHint = schema.Title;
            }

            String name = ConversionHelper.ConvertToUpperCamelCase(typeNameHint?.Split('.').Last() ?? "Anonymous", true);
            return name;
        }
    }

  public void GenerateCode(JsonSchema4 schema)
  {
        CSharpGeneratorSettings settings = new CSharpGeneratorSettings();
        settings.GenerateImmutableDictionaryProperties = true;
        settings.GenerateImmutableArrayProperties = true;
        settings.Namespace = "My.Namespace";
        settings.PropertyNameGenerator = new MyPropertyNameGenerator();
        settings.TypeNameGenerator = new MyTypeNameGenerator();

        CSharpGenerator jobGenerator = new CSharpGenerator(schema, settings);
 }

RodneyRichardson avatar Mar 07 '19 09:03 RodneyRichardson

The Xml property: https://github.com/RSuter/NJsonSchema/blob/master/src/NJsonSchema/JsonSchema4.cs#L470

The JsonXmlObject name: https://github.com/RSuter/NJsonSchema/blob/master/src/NJsonSchema/JsonXmlObject.cs#L22

I'm wondering why the setter is internal? @emilw do you know that?

RicoSuter avatar Mar 07 '19 13:03 RicoSuter

Hi @RicoSuter , Sorry for late response, i don't remember fully but it is most likely due to that it's just implemented for one way e.g. C# to Swagger spec. All these properties are created by that internal logic. I guess it should be safe to expose them, but again...there is no support for going the other way as i see it.

emilw avatar Apr 06 '19 06:04 emilw

@VictorioBerra I'm sorry to bump the issue off-topic, but I'm too curious about how, and where did you obtain the ETrade swagger file link and if there are any more of them?

iamwavecut avatar Jan 04 '24 15:01 iamwavecut

It's been a very long time so I do not fully remember. But if I recall when you get access to the etrade developer portal there are links there for it.

VictorioBerra avatar Jan 04 '24 15:01 VictorioBerra