quicktype icon indicating copy to clipboard operation
quicktype copied to clipboard

Generated C# code doesn't seem to consider allOf

Open lupino3 opened this issue 2 years ago • 3 comments

I am trying to generate C# code for a schema with conditional subschemas, that I'm expressing with allOf and if, but I cannot find those conditional subschemas in the output source code.

We can reproduce it by running the example in the JSON Schema documentation through QuickType:

{
  "type": "object",
  "properties": {
    "street_address": {
      "type": "string"
    },
    "country": {
      "default": "United States of America",
      "enum": ["United States of America", "Canada"]
    }
  },
  "if": {
    "properties": { "country": { "const": "United States of America" } }
  },
  "then": {
    "properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
  },
  "else": {
    "properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
  }
}

The resulting C# output does not contain any reference to postal_code:

// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
//    using QuickType;
//
//    var test = Test.FromJson(jsonString);

namespace QuickType
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class Test
    {
        [JsonProperty("country", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
        public virtual Country? Country { get; set; }

        [JsonProperty("street_address", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
        public virtual string StreetAddress { get; set; }
    }

    public enum Country { Canada, Netherlands, UnitedStatesOfAmerica };

    public partial class Test
    {
        public static Test FromJson(string json) => JsonConvert.DeserializeObject<Test>(json, QuickType.Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this Test self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters =
            {
                CountryConverter.Singleton,
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }

    internal class CountryConverter : JsonConverter
    {
        public override bool CanConvert(Type t) => t == typeof(Country) || t == typeof(Country?);

        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null) return null;
            var value = serializer.Deserialize<string>(reader);
            switch (value)
            {
                case "Canada":
                    return Country.Canada;
                case "Netherlands":
                    return Country.Netherlands;
                case "United States of America":
                    return Country.UnitedStatesOfAmerica;
            }
            throw new Exception("Cannot unmarshal type Country");
        }

        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
        {
            if (untypedValue == null)
            {
                serializer.Serialize(writer, null);
                return;
            }
            var value = (Country)untypedValue;
            switch (value)
            {
                case Country.Canada:
                    serializer.Serialize(writer, "Canada");
                    return;
                case Country.Netherlands:
                    serializer.Serialize(writer, "Netherlands");
                    return;
                case Country.UnitedStatesOfAmerica:
                    serializer.Serialize(writer, "United States of America");
                    return;
            }
            throw new Exception("Cannot marshal type Country");
        }

        public static readonly CountryConverter Singleton = new CountryConverter();
    }
}

lupino3 avatar Jul 28 '23 13:07 lupino3

The same happens if I quickly inspect code generated in other languages such as golang:

// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse and unparse this JSON data, add this code to your project and do:
//
//    test, err := UnmarshalTest(bytes)
//    bytes, err = test.Marshal()

package main

import "encoding/json"

func UnmarshalTest(data []byte) (Test, error) {
	var r Test
	err := json.Unmarshal(data, &r)
	return r, err
}

func (r *Test) Marshal() ([]byte, error) {
	return json.Marshal(r)
}

type Test struct {
	Country       *Country `json:"country,omitempty"`
	StreetAddress *string  `json:"street_address,omitempty"`
}

type Country string

const (
	Canada                Country = "Canada"
	Netherlands           Country = "Netherlands"
	UnitedStatesOfAmerica Country = "United States of America"

lupino3 avatar Jul 28 '23 13:07 lupino3

lol, expecting conditional subschemas to work on a project with 400+ issues

chadgrant avatar Aug 25 '23 04:08 chadgrant