根據swagger.json生成flutter model,暫無空安全支持

一般的服務端類型都有泛型支持,對於flutter來說雖然也支持泛型,但是在序列化這裡卻始終存在問題,flutter不允許用反射,對於flutter項目的開發來說除了畫頁面,可能最煩人的就是跟服務端打交道的時候對對象創建以及序列化,雖然目前網上也有通過json to dart之類的在線工具根據json生成model,但一個項目中那麼多類,都這麼做一遍太費勁,在有上下級類的情況下需要手動去一個個的調整,煩人,於是寫了一個小工具通過 swagger.json 生成flutter model。

1,首先在C#中 我們知道可以的可以生成代碼的有razor模板和T4模板,我是基於.net 5開發的於是就順其自然的使用了當下流行的razor模板來生成代碼

在代碼引入類庫RazorEngine.NetCore

 var config = new TemplateServiceConfiguration();
config.Language = Language.CSharp; // CSharp.NET as template language.
config.AllowMissingPropertiesOnDynamic = true;
config.CachingProvider = new DefaultCachingProvider(t => { });
config.EncodedStringFactory = new HtmlEncodedStringFactory(); // Html encoding.
config.Debug = false;
var service = RazorEngineService.Create(config);
var enummodel= File.ReadAllText("fluttermodel.cshtml");
var  result = Engine.Razor.RunCompile(enummodel, "fluttermodel", null, modelsinfo[i]);

result 就是根據模板生成的最終代碼,代碼量非常少,代碼生成中最主要的地方在於傳入template的model,model中需要定義一個dart類中需要用到的所有信息,

2 template fluttermodel.cshtml

@using flutter_model_genrate_swagger;
@using System.Linq;
@using System.Text;
@{
    var modeldes = "";
    if (!string.IsNullOrEmpty(Model.Description))
    {
        modeldes = string.Concat("///", Model.Description);
    }
    var modelparams = "";//參數

    var package = "";
    if (Model.Package != null && Model.Package.Count > 0)
    {
        foreach (var pk in Model.Package)
        {
            package += string.Concat("import '", pk, ".dart';\n");
        }
    }
}


@Raw(package)
@Raw(modeldes)
class @Model.Name {
@foreach (var proptey in Model.ModelPropties)
{
    if (!string.IsNullOrEmpty(proptey.Description))
    { 
        @Raw(string.Concat("///", proptey.Description,"\n"))   
    }
    @switch (proptey.Type)
    {
        case "array":
            @{
                string type = string.Concat("List<", @proptey.SubType, ">");
            }
            @Raw(type) @proptey.LowCaseName@(";")
            break;
        case "integer":
            @("int ") @proptey.LowCaseName@(";")
            break;
        case "string":
            @("String ") @proptey.LowCaseName@(";")
            break;
        case "boolean":
            @("bool ") @proptey.LowCaseName@(";")
            break;
        case "number":
            if (proptey.Format == "double")
            {
                @("double ") @proptey.LowCaseName@(";")
            }
            else
            {
                @Raw("///類型不確定")
                @("double ") @proptey.LowCaseName@(";")
            }
            break;
        default:
            @Raw(proptey.Type + " ")@proptey.LowCaseName@(";")
            break;
    }
    @Raw("\n");
    if (string.IsNullOrEmpty(modelparams))
    {
        modelparams += string.Concat("this.", proptey.LowCaseName);
    }
    else
    {
        modelparams += string.Concat(",this.", proptey.LowCaseName);
    }
}
@Model.Name @("({")@modelparams@("});")
@Raw("\n")
@Model.Name@Raw(".fromJson(Map<String, dynamic> json) {") @Raw("\n")
@foreach (var proptey in Model.ModelPropties)
{
    @switch (proptey.Type)
    {
        case "array":
            @{
                string type = string.Concat("List<", @proptey.SubType, ">");
            }
            @Raw("if (json['")@proptey.Name@Raw("'] != null) {")@Raw("\n")
            @proptey.LowCaseName@Raw("=[];")@Raw("\n")
            @Raw("json['")@proptey.Name@Raw("'].forEach((v) {")@Raw("\n")
            @if (proptey.SubTypeDes == "baseType")
            {
                @proptey.LowCaseName@(".add(v);")@Raw("\n")
            }
            else
            {
                @proptey.LowCaseName@(".add(")@proptey.SubType@Raw(".fromJson(v));")@Raw("\n")
            }
            @Raw("});\n")
            @Raw(" } else {\n")
            @proptey.LowCaseName@Raw("=[];\n")
            @Raw("}\n")
            break;
        case "integer":
        case "string":
        case "boolean":
        case "number":
            @proptey.LowCaseName@("=")@Raw("json['") @proptey.Name@Raw("'];")
            break;
        default:
            //判斷類型的
            if (proptey.TypeDes == "object")
            {
                @proptey.LowCaseName@Raw("=json['")@proptey.Name@Raw("']!=null? ")@proptey.Type@Raw(".fromJson(json['")@proptey.Name@Raw("']) : null;\n")
            }
            else
            {
                @proptey.LowCaseName@("=")@Raw("json['") @proptey.Name@Raw("'];")
            }
            break;
    }
    @Raw("\n");
}
}
@Raw("Map<String, dynamic> toJson() { \n")
@Raw("final Map<String, dynamic> data = new Map<String, dynamic>();\n")
@foreach (var proptey in Model.ModelPropties)
{
    switch (proptey.Type)
    {
        case "integer":
        case "string":
        case "boolean":
        case "number":
            @Raw("data['")@proptey.Name@Raw("'] = this.")@proptey.LowCaseName@Raw(";\n")
            break;
        case "array":
            //data['Data'] = this.data.map((v) => v.toJson()).toList();
            @Raw("if (this.")@proptey.LowCaseName@Raw(" != null) { \n")

            @if (proptey.SubTypeDes == "baseType")
            {
                @Raw("data['")@proptey.Name@Raw("'] = this.")@proptey.LowCaseName@Raw(".toList();\n")
            }
            else
            {
                @Raw("data['")@proptey.Name@Raw("'] = this.")@proptey.LowCaseName@Raw(".map((v) => v.toJson()).toList();\n")
            }
            @Raw("}\n")

            break;//普通對象
        default:
            if (proptey.TypeDes == "object")
            {

                @Raw("if (this.")@proptey.LowCaseName@Raw(" != null) { \n")
                @Raw("data['")@proptey.Name@Raw("'] = this.")@proptey.LowCaseName@Raw(".toJson();\n")
                @Raw("}\n")
            }
            else
            {
                @Raw("data['")@proptey.Name@Raw("'] = this.")@proptey.LowCaseName@Raw(";\n")
            }
            break;
    }
}
@Raw("return data;\n")
@Raw("}\n")

}

3 傳入template的類定義

public enum ModelInfoType
    { 
        Obj,
        Enum
    }
    public class ModelInfo
    {
        public ModelInfo() {
            ModelPropties = new List<ModelPropty>();
        }
        public ModelInfoType Type { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public List<ModelPropty> ModelPropties { get; set; }

        public string LowCaseName
        {
            get
            {
                if (string.IsNullOrEmpty(Name) || Name.Length < 2)
                {
                    return Name;
                }
                else
                { 
                    return Name.Substring(0, 1).ToLower() + Name.Substring(1);
                }
            }
        }
        public List<string> Package
        {
            get
            {
                if (this.ModelPropties.Count == 0)
                {
                    return null;
                }
                else
                {
                    var result = new List<string>();
                    foreach (var item in ModelPropties)
                    {
                        if (item.Type == "array")//包含arry
                        {
                            if (string.IsNullOrEmpty(item.SubType))
                            {
                                if (!ServiceAgent.BaseTypes.Contains(item.Type))
                                {
                                    result.Add(item.Type.Substring(0, 1).ToLower() + item.Type.Substring(1));
                                }
                               
                            }
                            else
                            {
                                if (!ServiceAgent.BaseTypes.Contains(item.SubType.ToLower()))
                                {
                                    result.Add(item.SubType.Substring(0, 1).ToLower() + item.SubType.Substring(1));
                                }
                            }
                        }
                        else if (!ServiceAgent.BaseTypes.Contains(item.Type))//基礎類型外
                        {
                            result.Add(item.Type.Substring(0, 1).ToLower() + item.Type.Substring(1));
                        }
                    }
                    return result;
                }
            }
        }


    }

    public class ModelPropty
    { 
        public string Name { get; set; }
        /// <summary>
        /// List<....> | 具體類型
        /// </summary>
        public string Type { get; set; }
        /// <summary>
        /// 如果是arry,這裡是arry的子類,
        /// </summary>
        public string SubType { get; set; }
        /// <summary>
        /// 子類描述
        /// </summary>
        public string SubTypeDes { get; set; }
        /// <summary>
        /// 類型描述
        /// </summary>
        public string TypeDes { get; set; }
        public string Format { get; set; }

        public string Description { get; set; }

        public string LowCaseName
        {
            get
            {
                if (string.IsNullOrEmpty(Name)|| Name.Length<2)
                {
                    return Name;
                }
                else
                {
                    return Name.Substring(0, 1).ToLower() + Name.Substring(1);
                }
            }
        }
    }

4 接下去的事情就簡單了,把swagger.json下載到本地,用system.text.json解析拿到swagger.json中的所有model,挨個生成

5 最後調用flutter format {文件夾位置} 將所有生成的model類格式化一遍,如果這裡發生錯誤,手動執行以下命令就大功告成了。

6 最後附上項目地址: //gitee.com/zzf_1/flutter-model-genrate-swagger

release版本下載地址://gitee.com/zzf_1/flutter-model-genrate-swagger/tags 【release版本稍微有點大,但是release版本不需要.net環境,對於專職flutter的開發人員來說這個應該會比較有用】