{"id":20123861,"url":"https://github.com/dalenewman/cfg-net","last_synced_at":"2025-05-06T16:34:08.343Z","repository":{"id":25327418,"uuid":"28754458","full_name":"dalenewman/Cfg-NET","owner":"dalenewman","description":"An Alternative .NET Configuration Handler","archived":false,"fork":false,"pushed_at":"2022-11-12T18:01:13.000Z","size":5663,"stargazers_count":22,"open_issues_count":1,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-18T13:21:05.437Z","etag":null,"topics":["configuration","configuration-handler","json","xml"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dalenewman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-03T20:55:55.000Z","updated_at":"2024-10-07T08:41:01.000Z","dependencies_parsed_at":"2023-01-14T03:30:21.175Z","dependency_job_id":null,"html_url":"https://github.com/dalenewman/Cfg-NET","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dalenewman%2FCfg-NET","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dalenewman%2FCfg-NET/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dalenewman%2FCfg-NET/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dalenewman%2FCfg-NET/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dalenewman","download_url":"https://codeload.github.com/dalenewman/Cfg-NET/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252721161,"owners_count":21793765,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","configuration-handler","json","xml"],"created_at":"2024-11-13T19:46:21.335Z","updated_at":"2025-05-06T16:34:07.575Z","avatar_url":"https://github.com/dalenewman.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"Cfg-NET\n=======\n\n[![Build status](https://ci.appveyor.com/api/projects/status/qm4auhkcv6b23abr?svg=true)](https://ci.appveyor.com/project/dalenewman/cfg-net)\n[![NuGet](https://img.shields.io/nuget/v/Cfg-NET.svg?label=Nuget)](https://www.nuget.org/packages/Cfg-NET)\n\nCfg-NET is a configuration library for .NET with \nbuilt-in validation and error reporting.\n\n### Configuration\n\nSupport for XML and JSON configurations are \nbuilt-in.  An example of fruits with their colors is provided below.\n\n```xml\n\u003ccfg id=\"1\"\u003e\n    \u003cfruit\u003e\n        \u003cadd name=\"apple\"\u003e\n            \u003ccolors\u003e\n                \u003cadd name=\"red\" /\u003e\n                \u003cadd name=\"yellow\" /\u003e\n                \u003cadd name=\"green\" /\u003e\n            \u003c/colors\u003e\n        \u003c/add\u003e\n        \u003cadd name=\"banana\"\u003e\n            \u003ccolors\u003e\n                \u003cadd name=\"yellow\" /\u003e\n            \u003c/colors\u003e\n        \u003c/add\u003e\n    \u003c/fruit\u003e\n\u003c/cfg\u003e\n```\nIn XML, collections are named elements containing nested `\u003cadd/\u003e` elements. The collection elements may not contain attributes. Only the root (e.g. `\u003ccfg/\u003e`) and the `\u003cadd/\u003e` elements may have attributes.\n\nHere's the same in JSON:\n\n```json\n{   \n   \"id\" : 1,\n   \"fruit\": [\n      { \n         \"name\":\"apple\",\n         \"colors\": [\n            {\"name\":\"red\"},\n            {\"name\":\"yellow\"},\n            {\"name\":\"green\"}\n         ]\n      },\n      {\n         \"name\":\"banana\",\n         \"colors\": [\n            {\"name\":\"yellow\"}\n         ]\n      }\n   ]\n}\n```\n\nIn JSON, collections are named *arrays* of *objects*.  The collections may not have properties. Only the root object or objects within a collection array may have properties.\n\n### Code\n\nIn code, a corresponding C# model for fruits \nand their colors looks like this:\n\n```csharp\nusing System.Collections.Generic;\n\nclass Cfg {\n   public int Id {get; set;}\n   public List\u003cFruit\u003e Fruit { get; set; }\n}\n\nclass Fruit {\n    public string Name { get; set; }\n    public List\u003cColor\u003e Colors {get; set;}\n}\n\nclass Color {\n    public string Name {get; set;}\n}\n```\n\nTo enable configurations, have each class \ninherit `CfgNode` and decorate the properties \nwith the `Cfg` custom attribute: \n\n```csharp\nusing System.Collections.Generic;\nusing Cfg.Net;\n\nclass Cfg : CfgNode {\n   [Cfg]\n   public int Id { get; set;}      \n   [Cfg]\n   public List\u003cFruit\u003e Fruit { get; set; }\n}\n\nclass Fruit : CfgNode {\n    [Cfg]\n    public string Name { get; set; }\n    [Cfg]\n    public List\u003cColor\u003e Colors {get; set;}\n}\n\nclass Color : CfgNode {\n    [Cfg]\n    public string Name {get; set;}\n}\n```\n \n### Design the Configuration\n\nInheriting from `CfgNode` provides base methods:\n\n* `Load()` for loading or checking a configuration\n* `Errors()` and `Warnings()` to check after `Load` is called\n* `Serialize()` to get a text representation \n\nThe `Cfg` attribute adds validation and modification \ninstructions.  It has these options:\n\n* validation\n  * `required`\n  * `unique`\n  * `domain` with `delimiter` and `ignoreCase` options\n  * `minLength` and/or `maxLength`\n  * `minValue` and/or `maxValue`\n  * `regex` with `ignoreCase` option\n* transformation\n  * `value`, as in _default_ value\n  * `toLower` or `toUpper`\n  * `trim`, `trimStart`, or `trimEnd`\n\n---\n\nTo make sure some fruit is defined in our configuration, we\nwould add `required=true` to the fruit list like this:\n\n```csharp\nclass Cfg : CfgNode {\n    [Cfg(required=true)] // THERE MUST BE SOME FRUIT!\n    public List\u003cFruit\u003e Fruit { get; set; }\n}\n```\nIf we want to make sure the fruit names are unique, we could \nadd `unique=true` to the fruit name attribute like this:  \n\n```csharp\nclass Fruit : CfgNode {\n    [Cfg(unique=true)] // THE FRUIT MUST BE UNIQUE!\n    public string Name { get; set; }\n    [Cfg]\n    public List\u003cColor\u003e Colors {get; set;}\n}\n```\n\nIf we want to control what colors are used, we could \nadd `domain=\"red,green,etc\"` to the color name attribute like this:\n\n```csharp\nclass Color : CfgNode {\n    [Cfg(domain=\"red,yellow,green,blue,purple,orange\")]\n    public string Name {get; set;}\n}\n```\n\n### Load the Configuration\n\nLoad the configuration into the model like this:\n\n```csharp\n// let xml be your configuration\nvar cfg = new Cfg();\ncfg.Load(xml);\n```\n\n### Examine for Errors and/or Warnings\n\nAfter loading, always examine your model for any \nissues using the `Errors()` \nand `Warnings()` methods:\n\n```csharp\n//LOAD CONFIGURATION\nvar cfg = new Cfg();\ncfg.Load(xml);\n\n/* CHECK FOR WARNINGS */\nAssert.AreEqual(0, cfg.Warnings().Length);\n\n/* CHECK FOR ERRORS */\nAssert.AreEqual(0, cfg.Errors().Length);\n\n/* EVERYTHING IS AWESOME!!! */\n```\n\nBy convention, an error means the configuration is invalid.\nA warning is something you ought to address, but the program\nshould still work.\n\nErrors and warnings should be reported to the end-user\nso they can fix them. Here are some example errors:\n\nRemove the required fruit and...\n\n\u003e **fruit** must be populated in **cfg**.\n\nAdd another apple and...\n\n\u003e Duplicate **name** value **apple** in **fruit**.\n\nAdd the color pink...\n\n\u003e An invalid value of **pink** is in **name**.  The valid domain is: red, yellow, green, purple, blue, orange.\n\nIf Cfg-NET doesn't report issues, your configuration \nis valid.  You can loop through your fruits and their \ncolors without a care in the world:\n\n```csharp\nvar cfg = new Cfg();\ncfg.Load(xml);\n    \nforeach (var fruit in cfg.Fruit) {\n    foreach (var color in fruit.Colors) {\n        /* use fruit.Name and color.Name... */  \n    }\n}\n```\n\nYou never have to worry about a `Cfg` decorated list being `null` \nbecause it is initialized as the configuration loads.  Moreover, \nif you set default values (e.g. `[Cfg(value=\"default\")]`), a \nproperty is never `null`.\n\nPlay with the apples and bananas on [.NET Fiddle](https://dotnetfiddle.net/slRAf3).\n\nCustomization\n---------------------------\n\nThe `Cfg` attribute's optional properties \noffer simple validation and transformation. \nIf it's not enough, you have options:\n\n1. Overriding `PreValidate()`\n1. Overriding `Validate()`\n1. Overriding `PostValidate()`\n\n### PreValidate()\n\nIf you want to modify a configuration before \nvalidation, override `PreValidate()` like this:\n\n```csharp\nprotected override void PreValidate() {\n    if (Provider == \"Bad Words\") {\n        Provider = \"Good Words\";\n        Warn(\"Watch your language!\");\n    }\n}\n```\n\n### Validate()\n\nTo perform validation involving more than\none property, override `Validate()` like this:\n\n```csharp\npublic class Connection : CfgNode {\n    [Cfg(required = true, domain = \"file,folder,other\")]\n    public string Provider { get; set; }\n    \n    [Cfg()]\n    public string File { get; set; }\n    \n    [Cfg()]\n    public string Folder { get; set; }\n    \n    /* CUSTOM VALIDATION */\n    protected override void Validate() {\n        if (Provider == \"file\" \u0026\u0026 string.IsNullOrEmpty(File)) {\n            Error(\"file provider needs file attribute.\");\n        } else if (Provider == \"folder\" \u0026\u0026 string.IsNullOrEmpty(Folder)) {\n            Error(\"folder provider needs folder attribute.\");\n        }\n    }\n}\n```\n\nWhen you override `Validate`, add issues using\nthe `Error()` and `Warn()` methods.\n\n### PostValidate()\n\nOverriding `PostValidate` gives you an opportunity \nto run code after validation.  You may check `Errors()` \nand/or `Warnings()` and make further preparations. \n\n```csharp\nprotected override void PostValidate() {\n    if (Errors().Length == 0) {\n        /* make further preparations... */\n    }\n}\n```\n\n### Customization\n\nIf the attributes and methods aren't enough, \nyou may inject customizers (e.g. things \nimplementing `ICustomizer`) into \nyour model's contructor.\n\n### Serialize\n\nAfter your configuration is loaded into code, you \ncan serialize it back to a string with `Serialize()`.\n\n```csharp\n// load\nvar cfg = new Cfg();\ncfg.Load(xml);\n\n// modify\ncfg.Fruit.RemoveAll(f =\u003e f.Name == \"apple\");\ncfg.Fruit.Add(new Fruit {\n    Name = \"plum\",\n    Colors = new List\u003cColor\u003e {\n        new Color { Name = \"purple\" }\n    }\n});\n\n// serialize\nvar result = cfg.Serialize();\n```\n\nThis produces a result of:\n\n```xml\n\u003ccfg\u003e\n    \u003cfruit\u003e\n        \u003cadd name=\"banana\"\u003e\n            \u003ccolors\u003e\n                \u003cadd name=\"yellow\" /\u003e\n            \u003c/colors\u003e\n        \u003c/add\u003e\n        \u003cadd name=\"plum\"\u003e\n            \u003ccolors\u003e\n                \u003cadd name=\"purple\" /\u003e\n            \u003c/colors\u003e\n        \u003c/add\u003e\n    \u003c/fruit\u003e\n\u003c/cfg\u003e\n```\n\n**Note**: If you loaded XML, it serializes to XML. \nIf you loaded JSON, it serializes to JSON.\n\n### Configure with Code\n\nSometimes you need to write a configuration in \ncode.  If you do this, be sure to call `Load()` \nwithout parameters.\n\n```csharp\nvar cfg = new Cfg {\n    Fruit = new List\u003cFruit\u003e {\n        new Fruit {\n            Name = \"Apple\",\n            Colors = new List\u003cColor\u003e {\n                new Color {Name = \"red\"},\n                new Color {Name = \"aqua\"}\n            }\n        }\n    }\n};\n\n// Call Load() to check for errors and warnings\ncfg.Load();\n\n// I put an error in there on purpose (hint: aqua is invalid)\nAssert.AreEqual(1, cfg.Errors().Length);\n```\n\n### Conclusion\nSo, if you need configurations for your programs, \ngive Cfg-NET a try. I use it in all the programs \nI write, and I am very happy with it. \nThank you for taking the time to read this. \nI appreciate the stars and feedback.\n\n### Credits\n*  a modified version of `NanoXmlParser` found [here](http://www.codeproject.com/Tips/682245/NanoXML-Simple-and-fast-XML-parser).\n*  a modified version of `fastJSON` found [here](http://www.codeproject.com/Articles/159450/fastJSON)\n*  .NET Source of `WebUtility.HtmlDecode` found [here](http://referencesource.microsoft.com/#System/net/System/Net/WebUtility.cs), used as reference.\n\n#### Further Reading (optional)\n\n* [Using Dependency Injection \u0026 Autofac with Cfg-NET](https://github.com/dalenewman/Cfg-NET/blob/master/Articles/Autofac.md)\n* [Using Environments, Parameters, and @(Place-Holders)](https://github.com/dalenewman/Cfg-NET/blob/master/Articles/EnvironmentsAndParameters.md)\n* [Using Shorthand](https://github.com/dalenewman/Cfg-NET/blob/master/Articles/Shorthand.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdalenewman%2Fcfg-net","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdalenewman%2Fcfg-net","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdalenewman%2Fcfg-net/lists"}