1

I have a json schemas in a project and want to add build step to generate classes from them, on of these schemas contains an array of objects and strings, simplified example below:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "testSchema",
    "type": "object",
    "properties": {
        "array": {
            "type": "array",
            "items": {
                "anyOf": [
                    {
                        "type": "string"
                    },
                    {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string"
                            }
                        }
                    }
                ]
            }
        }
    }
}

I'm using NJsonSchema to generate C# code from this schema. As a result I'm getting the following output:

//----------------------
// <auto-generated>
//     Generated using the NJsonSchema v8.32.6319.16936 (http://NJsonSchema.org)
// </auto-generated>
//----------------------

namespace TestSchema
{
#pragma warning disable // Disable all warnings

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "8.32.6319.16936")]
public partial class TestSchema : System.ComponentModel.INotifyPropertyChanged
{
    private System.Collections.ObjectModel.ObservableCollection<Anonymous> _array = new System.Collections.ObjectModel.ObservableCollection<Anonymous>();

    [Newtonsoft.Json.JsonProperty("array", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public System.Collections.ObjectModel.ObservableCollection<Anonymous> Array
    {
        get { return _array; }
        set 
        {
            if (_array != value)
            {
                _array = value; 
                RaisePropertyChanged();
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    public string ToJson() 
    {
        return Newtonsoft.Json.JsonConvert.SerializeObject(this);
    }

    public static TestSchema FromJson(string data)
    {
        return Newtonsoft.Json.JsonConvert.DeserializeObject<TestSchema>(data);
    }

    protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) 
            handler(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "8.32.6319.16936")]
public partial class Anonymous : System.ComponentModel.INotifyPropertyChanged
{

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    public string ToJson() 
    {
        return Newtonsoft.Json.JsonConvert.SerializeObject(this);
    }

    public static Anonymous FromJson(string data)
    {
        return Newtonsoft.Json.JsonConvert.DeserializeObject<Anonymous>(data);
    }

    protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) 
            handler(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}
}

As a result I have this strange Anonymous class and if I try to deserialise a json file below I get an error: string can't be converted into Anonymous. To deserialise I use the following generated method:

TestSchema.FromJson

Is that possible to adjust code generation to have as a result a collection of object instead and to get deserialised objects with correct types in it?

{
    "array": [
        "stringItem1",
        {
            "name": "complexObj1"
        }
    ]
}
1
  • 2
    My favorit JSON to C# converter is json2csharp.com I have generated your JSON and it looks like a better representation of your JSON as your class. Perhaps, it can be help you? Commented Apr 21, 2017 at 21:34

1 Answer 1

1

Finally I achieved what I needed.

The idea is to pass custom CSharpTypeResolver into CSharpGenerator:

new CSharpGenerator(jsonSchema4, settings, new CustomCSharpTypeResolver(settings, jsonSchema4), null);

Looks like it wasn't intended by NJsonSchema author. In CustomCSharpTypeResolver I override Resolve method to add the following behaviour:

if (schema.AnyOf.Count > 0)
    return "object";

As a result for simplified example I have the following model:

//----------------------
// <auto-generated>
//     Generated using the NJsonSchema v8.32.6319.16936 (http://NJsonSchema.org)
// </auto-generated>
//----------------------

namespace JsonSchemaClassGenerator.TestSchema
{
#pragma warning disable // Disable all warnings

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "8.32.6319.16936")]
public partial class TestSchema : System.ComponentModel.INotifyPropertyChanged
{
    private System.Collections.ObjectModel.ObservableCollection<object> _array = new System.Collections.ObjectModel.ObservableCollection<object>();

    [Newtonsoft.Json.JsonProperty("array", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public System.Collections.ObjectModel.ObservableCollection<object> Array
    {
        get { return _array; }
        set 
        {
            if (_array != value)
            {
                _array = value; 
                RaisePropertyChanged();
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    public string ToJson() 
    {
        return Newtonsoft.Json.JsonConvert.SerializeObject(this);
    }

    public static TestSchema FromJson(string data)
    {
        return Newtonsoft.Json.JsonConvert.DeserializeObject<TestSchema>(data);
    }

    protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) 
            handler(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "8.32.6319.16936")]
public partial class Object : System.ComponentModel.INotifyPropertyChanged
{
    private string _name;

    [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public string Name
    {
        get { return _name; }
        set 
        {
            if (_name != value)
            {
                _name = value; 
                RaisePropertyChanged();
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    public string ToJson() 
    {
        return Newtonsoft.Json.JsonConvert.SerializeObject(this);
    }

    public static Object FromJson(string data)
    {
        return Newtonsoft.Json.JsonConvert.DeserializeObject<Object>(data);
    }

    protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) 
            handler(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}
}

Deserialisation works fine. I can cast objects as I want. There is only one problem: objects are being saved as JObject instances so I need to implement explicit or implicit operator to convert it into generated model.

namespace JsonSchemaClassGenerator.TestSchema
{
    public partial class Object
    {
        public static implicit operator Object(JObject json)
        {
            return FromJson(json.ToString());
        }
    }
}

After that it will be possible to convert JObject into generated model (Object is not a System.Object it just was generated with such a name):

Object a = config.Entries[1] as JObject;

It is the simplest solution I found. I think it is also possible to implement custom CSharpTypeResolver to have something more type-safe. But not sure if I will try since for me it looks like it would be better to make NJsonSchema more flexible first.

Sign up to request clarification or add additional context in comments.

2 Comments

I'm the author of NJS. The resolver is meant to be extended to support such cases (see github.com/NSwag/NSwag/blob/master/src/…). If you need more extension points, please create an issue on github and we can discuss there...
@RicoSuter thank you for the feedback, will try to do so

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.