Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dalenewman/cfg-net
An Alternative .NET Configuration Handler
https://github.com/dalenewman/cfg-net
configuration configuration-handler json xml
Last synced: 3 months ago
JSON representation
An Alternative .NET Configuration Handler
- Host: GitHub
- URL: https://github.com/dalenewman/cfg-net
- Owner: dalenewman
- License: apache-2.0
- Created: 2015-01-03T20:55:55.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2022-11-12T18:01:13.000Z (about 2 years ago)
- Last Synced: 2024-11-09T06:54:54.958Z (3 months ago)
- Topics: configuration, configuration-handler, json, xml
- Language: C#
- Homepage:
- Size: 5.4 MB
- Stars: 22
- Watchers: 7
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
Cfg-NET
=======[![Build status](https://ci.appveyor.com/api/projects/status/qm4auhkcv6b23abr?svg=true)](https://ci.appveyor.com/project/dalenewman/cfg-net)
[![NuGet](https://img.shields.io/nuget/v/Cfg-NET.svg?label=Nuget)](https://www.nuget.org/packages/Cfg-NET)Cfg-NET is a configuration library for .NET with
built-in validation and error reporting.### Configuration
Support for XML and JSON configurations are
built-in. An example of fruits with their colors is provided below.```xml
```
In XML, collections are named elements containing nested `` elements. The collection elements may not contain attributes. Only the root (e.g. ``) and the `` elements may have attributes.Here's the same in JSON:
```json
{
"id" : 1,
"fruit": [
{
"name":"apple",
"colors": [
{"name":"red"},
{"name":"yellow"},
{"name":"green"}
]
},
{
"name":"banana",
"colors": [
{"name":"yellow"}
]
}
]
}
```In 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.
### Code
In code, a corresponding C# model for fruits
and their colors looks like this:```csharp
using System.Collections.Generic;class Cfg {
public int Id {get; set;}
public List Fruit { get; set; }
}class Fruit {
public string Name { get; set; }
public List Colors {get; set;}
}class Color {
public string Name {get; set;}
}
```To enable configurations, have each class
inherit `CfgNode` and decorate the properties
with the `Cfg` custom attribute:```csharp
using System.Collections.Generic;
using Cfg.Net;class Cfg : CfgNode {
[Cfg]
public int Id { get; set;}
[Cfg]
public List Fruit { get; set; }
}class Fruit : CfgNode {
[Cfg]
public string Name { get; set; }
[Cfg]
public List Colors {get; set;}
}class Color : CfgNode {
[Cfg]
public string Name {get; set;}
}
```
### Design the ConfigurationInheriting from `CfgNode` provides base methods:
* `Load()` for loading or checking a configuration
* `Errors()` and `Warnings()` to check after `Load` is called
* `Serialize()` to get a text representationThe `Cfg` attribute adds validation and modification
instructions. It has these options:* validation
* `required`
* `unique`
* `domain` with `delimiter` and `ignoreCase` options
* `minLength` and/or `maxLength`
* `minValue` and/or `maxValue`
* `regex` with `ignoreCase` option
* transformation
* `value`, as in _default_ value
* `toLower` or `toUpper`
* `trim`, `trimStart`, or `trimEnd`---
To make sure some fruit is defined in our configuration, we
would add `required=true` to the fruit list like this:```csharp
class Cfg : CfgNode {
[Cfg(required=true)] // THERE MUST BE SOME FRUIT!
public List Fruit { get; set; }
}
```
If we want to make sure the fruit names are unique, we could
add `unique=true` to the fruit name attribute like this:```csharp
class Fruit : CfgNode {
[Cfg(unique=true)] // THE FRUIT MUST BE UNIQUE!
public string Name { get; set; }
[Cfg]
public List Colors {get; set;}
}
```If we want to control what colors are used, we could
add `domain="red,green,etc"` to the color name attribute like this:```csharp
class Color : CfgNode {
[Cfg(domain="red,yellow,green,blue,purple,orange")]
public string Name {get; set;}
}
```### Load the Configuration
Load the configuration into the model like this:
```csharp
// let xml be your configuration
var cfg = new Cfg();
cfg.Load(xml);
```### Examine for Errors and/or Warnings
After loading, always examine your model for any
issues using the `Errors()`
and `Warnings()` methods:```csharp
//LOAD CONFIGURATION
var cfg = new Cfg();
cfg.Load(xml);/* CHECK FOR WARNINGS */
Assert.AreEqual(0, cfg.Warnings().Length);/* CHECK FOR ERRORS */
Assert.AreEqual(0, cfg.Errors().Length);/* EVERYTHING IS AWESOME!!! */
```By convention, an error means the configuration is invalid.
A warning is something you ought to address, but the program
should still work.Errors and warnings should be reported to the end-user
so they can fix them. Here are some example errors:Remove the required fruit and...
> **fruit** must be populated in **cfg**.
Add another apple and...
> Duplicate **name** value **apple** in **fruit**.
Add the color pink...
> An invalid value of **pink** is in **name**. The valid domain is: red, yellow, green, purple, blue, orange.
If Cfg-NET doesn't report issues, your configuration
is valid. You can loop through your fruits and their
colors without a care in the world:```csharp
var cfg = new Cfg();
cfg.Load(xml);
foreach (var fruit in cfg.Fruit) {
foreach (var color in fruit.Colors) {
/* use fruit.Name and color.Name... */
}
}
```You never have to worry about a `Cfg` decorated list being `null`
because it is initialized as the configuration loads. Moreover,
if you set default values (e.g. `[Cfg(value="default")]`), a
property is never `null`.Play with the apples and bananas on [.NET Fiddle](https://dotnetfiddle.net/slRAf3).
Customization
---------------------------The `Cfg` attribute's optional properties
offer simple validation and transformation.
If it's not enough, you have options:1. Overriding `PreValidate()`
1. Overriding `Validate()`
1. Overriding `PostValidate()`### PreValidate()
If you want to modify a configuration before
validation, override `PreValidate()` like this:```csharp
protected override void PreValidate() {
if (Provider == "Bad Words") {
Provider = "Good Words";
Warn("Watch your language!");
}
}
```### Validate()
To perform validation involving more than
one property, override `Validate()` like this:```csharp
public class Connection : CfgNode {
[Cfg(required = true, domain = "file,folder,other")]
public string Provider { get; set; }
[Cfg()]
public string File { get; set; }
[Cfg()]
public string Folder { get; set; }
/* CUSTOM VALIDATION */
protected override void Validate() {
if (Provider == "file" && string.IsNullOrEmpty(File)) {
Error("file provider needs file attribute.");
} else if (Provider == "folder" && string.IsNullOrEmpty(Folder)) {
Error("folder provider needs folder attribute.");
}
}
}
```When you override `Validate`, add issues using
the `Error()` and `Warn()` methods.### PostValidate()
Overriding `PostValidate` gives you an opportunity
to run code after validation. You may check `Errors()`
and/or `Warnings()` and make further preparations.```csharp
protected override void PostValidate() {
if (Errors().Length == 0) {
/* make further preparations... */
}
}
```### Customization
If the attributes and methods aren't enough,
you may inject customizers (e.g. things
implementing `ICustomizer`) into
your model's contructor.### Serialize
After your configuration is loaded into code, you
can serialize it back to a string with `Serialize()`.```csharp
// load
var cfg = new Cfg();
cfg.Load(xml);// modify
cfg.Fruit.RemoveAll(f => f.Name == "apple");
cfg.Fruit.Add(new Fruit {
Name = "plum",
Colors = new List {
new Color { Name = "purple" }
}
});// serialize
var result = cfg.Serialize();
```This produces a result of:
```xml
```
**Note**: If you loaded XML, it serializes to XML.
If you loaded JSON, it serializes to JSON.### Configure with Code
Sometimes you need to write a configuration in
code. If you do this, be sure to call `Load()`
without parameters.```csharp
var cfg = new Cfg {
Fruit = new List {
new Fruit {
Name = "Apple",
Colors = new List {
new Color {Name = "red"},
new Color {Name = "aqua"}
}
}
}
};// Call Load() to check for errors and warnings
cfg.Load();// I put an error in there on purpose (hint: aqua is invalid)
Assert.AreEqual(1, cfg.Errors().Length);
```### Conclusion
So, if you need configurations for your programs,
give Cfg-NET a try. I use it in all the programs
I write, and I am very happy with it.
Thank you for taking the time to read this.
I appreciate the stars and feedback.### Credits
* a modified version of `NanoXmlParser` found [here](http://www.codeproject.com/Tips/682245/NanoXML-Simple-and-fast-XML-parser).
* a modified version of `fastJSON` found [here](http://www.codeproject.com/Articles/159450/fastJSON)
* .NET Source of `WebUtility.HtmlDecode` found [here](http://referencesource.microsoft.com/#System/net/System/Net/WebUtility.cs), used as reference.#### Further Reading (optional)
* [Using Dependency Injection & Autofac with Cfg-NET](https://github.com/dalenewman/Cfg-NET/blob/master/Articles/Autofac.md)
* [Using Environments, Parameters, and @(Place-Holders)](https://github.com/dalenewman/Cfg-NET/blob/master/Articles/EnvironmentsAndParameters.md)
* [Using Shorthand](https://github.com/dalenewman/Cfg-NET/blob/master/Articles/Shorthand.md)