Skip to content

Commit a73f8ae

Browse files
committed
chore: documentation
1 parent 1e89139 commit a73f8ae

8 files changed

Lines changed: 604 additions & 132 deletions

File tree

README.md

Lines changed: 81 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,107 @@
11
# ObjectSemantics.NET
22
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fswagfin%2FObjectSemantics.NET.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fswagfin%2FObjectSemantics.NET?ref=badge_shield)
33

4-
**Simple and flexible object-to-template string mapper with formatting support**
5-
6-
## 🧠 Overview
7-
8-
**ObjectSemantics.NET** is a lightweight C# library that lets you inject object property values directly into string templates much like [Handlebars](https://handlebarsjs.com/) or Helm templates, but focused on .NET.
9-
10-
This is especially useful when you want to dynamically generate content such as:
11-
- Email templates
12-
- HTML fragments
13-
- Reports or invoices
14-
- Config files
15-
- Logging output
16-
---
17-
18-
## 📦 Installation
19-
4+
Object-to-template mapping for .NET with nested property support, loops, conditions, formatting, and lightweight calculations.
5+
6+
## Why ObjectSemantics.NET
7+
Use it when you need fast, readable template mapping for:
8+
- Email and SMS templates
9+
- Receipts, invoices, and reports
10+
- Notification payloads
11+
- Config and log rendering
12+
13+
## Features
14+
- Direct property mapping: `{{ Name }}`
15+
- Nested property mapping: `{{ Customer.BankingDetail.BankName }}`
16+
- Collection loops: `{{ #foreach(Items) }}...{{ #endforeach }}`
17+
- Conditional blocks: `{{ #if(Age >= 18) }}...{{ #else }}...{{ #endif }}`
18+
- Built-in formatting for number/date/string
19+
- XML escaping option (`XmlCharEscaping = true`)
20+
- Calculation functions:
21+
- `sum`, `avg`, `count`, `min`, `max`
22+
- `calc` arithmetic expressions
23+
- Function names accept optional leading underscores: `_sum(...)`, `__calc(...)`
24+
25+
## Installation
2026
Install from [NuGet](https://www.nuget.org/packages/ObjectSemantics.NET):
2127

22-
```bash
28+
```powershell
2329
Install-Package ObjectSemantics.NET
2430
```
2531

26-
---
27-
28-
## 🚀 Quick Start
29-
30-
### Example 1: Mapping Object Properties
31-
32-
```csharp
33-
Person person = new Person
34-
{
35-
Name = "John Doe"
36-
};
37-
38-
// Define template and map it using the object
39-
string result = person.Map("I am {{ Name }}!");
40-
41-
Console.WriteLine(result);
42-
```
43-
44-
**Output:**
45-
```
46-
I am John Doe!
47-
```
48-
---
49-
50-
### Example 2: Mapping Using String Extension
51-
32+
## Quick Start
5233
```csharp
53-
Person person = new Person
54-
{
55-
Name = "Jane Doe"
56-
};
34+
using ObjectSemantics.NET;
5735

58-
// You can also start with the string template
59-
string result = "I am {{ Name }}!".Map(person);
60-
61-
Console.WriteLine(result);
62-
```
63-
64-
**Output:**
65-
```
66-
I am Jane Doe!
36+
var person = new Person { Name = "John Doe" };
37+
string output = person.Map("Hello {{ Name }}");
38+
// Hello John Doe
6739
```
68-
---
69-
70-
### Example 3: Mapping Enumerable Collections (Looping)
7140

41+
## Template Examples
42+
### Nested mapping
7243
```csharp
73-
Person person = new Person
44+
var payment = new CustomerPayment
7445
{
75-
MyCars = new List<Car>
46+
Amount = 100000000,
47+
Customer = new Customer
7648
{
77-
new Car { Make = "BMW", Year = 2023 },
78-
new Car { Make = "Rolls-Royce", Year = 2020 }
49+
CompanyName = "CRUDSOFT TECHNOLOGIES"
7950
}
8051
};
8152

82-
string template = @"
83-
{{ #foreach(MyCars) }}
84-
- {{ Year }} {{ Make }}
85-
{{ #endforeach }}";
86-
87-
string result = person.Map(template);
88-
89-
Console.WriteLine(result);
53+
string result = payment.Map("Paid Amount: {{ Amount:N2 }} By {{ Customer.CompanyName }}");
54+
// Paid Amount: 100,000,000.00 By CRUDSOFT TECHNOLOGIES
9055
```
9156

92-
**Output:**
93-
```
94-
- 2023 BMW
95-
- 2020 Rolls-Royce
96-
```
97-
---
98-
99-
### Example 4: Conditional Logic with `#if`, `#else`, and `#endif`
100-
57+
### Loop + format
10158
```csharp
102-
Person person = new Person
103-
{
104-
Age = 40
105-
};
106-
107-
string template = @"
108-
{{ #if(Age >= 18) }}
109-
Adult
110-
{{ #else }}
111-
Minor
112-
{{ #endif }}";
113-
114-
string result = person.Map(template);
115-
116-
Console.WriteLine(result);
59+
string template = "{{ #foreach(Items) }}[{{ Quantity }}x{{ Name }}={{ LineTotal:N2 }}]{{ #endforeach }}";
11760
```
11861

119-
**Output:**
120-
```
121-
Adult
122-
```
123-
---
124-
### Example 5: Number Formatting Support
125-
62+
### Condition
12663
```csharp
127-
Car car = new Car
128-
{
129-
Price = 50000
130-
};
131-
132-
string result = car.Map("{{ Price:#,##0 }} | {{ Price:N2 }}");
133-
134-
Console.WriteLine(result);
135-
```
136-
137-
**Output:**
64+
string template = "{{ #if(IsPaid == true) }}PAID{{ #else }}UNPAID{{ #endif }}";
13865
```
139-
50,000 | 50,000.00
140-
```
141-
---
142-
143-
## 💡 More Examples & Documentation
144-
145-
Explore more usage examples and edge cases in the Wiki Page:
14666

147-
📁 [`Wiki Page`](https://github.com/swagfin/ObjectSemantics.NET/wiki/%F0%9F%9B%A0-Usage-Guide)
148-
149-
---
150-
151-
## 🤝 Contributing
152-
153-
Feel free to open issues or contribute improvements via pull requests!
154-
155-
---
156-
157-
## 📄 MIT License
158-
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fswagfin%2FObjectSemantics.NET.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fswagfin%2FObjectSemantics.NET?ref=badge_large)
67+
### Calculations
68+
```csharp
69+
// Aggregates
70+
"{{ __sum(Customer.Payments.Amount):N2 }}"
71+
"{{ _avg(Customer.Payments.PaidAmount):N2 }}"
72+
"{{ __count(Customer.Payments.Amount) }}"
73+
74+
// Arithmetic expression
75+
"{{ __calc(PaidAmount - Customer.CreditLimit):N2 }}"
76+
```
77+
78+
Calculation behavior:
79+
- Null source/property in math path returns zero
80+
- Unknown property/path returns empty
81+
- Invalid/non-numeric math expression returns empty
82+
83+
## Documentation
84+
Detailed wiki files are available in-repo:
85+
- [Wiki Home](wiki/Home.md)
86+
- [Getting Started](wiki/Getting-Started.md)
87+
- [Template Syntax](wiki/Template-Syntax.md)
88+
- [Calculations](wiki/Calculations.md)
89+
- [Real-World Recipes](wiki/Recipes.md)
90+
- [Troubleshooting](wiki/Troubleshooting.md)
91+
92+
These files can be copied directly into your GitHub Wiki repository.
93+
94+
## Validation
95+
Current test suite covers:
96+
- Nested object mapping
97+
- Conditions and loops
98+
- String/number/date formatting
99+
- File template mapping
100+
- Real-world email and messaging scenarios
101+
- Expression functions and edge cases
102+
103+
## Contributing
104+
Contributions are welcome through issues and pull requests.
105+
106+
## License
107+
[MIT](LICENSE)

wiki/Calculations.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Calculations
2+
3+
ObjectSemantics.NET supports lightweight calculation expressions directly in templates.
4+
5+
## Supported Functions
6+
7+
You can use function names with or without leading underscores.
8+
9+
| Function | Purpose | Example |
10+
| --- | --- | --- |
11+
| `sum(path)` | Sum numeric values in a path | `{{ __sum(Customer.Payments.Amount):N2 }}` |
12+
| `avg(path)` | Average numeric values in a path | `{{ _avg(Customer.Payments.PaidAmount):N2 }}` |
13+
| `count(path)` | Count non-null values in a path | `{{ __count(Customer.Payments.Amount) }}` |
14+
| `min(path)` | Minimum numeric value in a path | `{{ __min(Customer.Payments.Amount):N2 }}` |
15+
| `max(path)` | Maximum numeric value in a path | `{{ __max(Customer.Payments.Amount):N2 }}` |
16+
| `calc(expr)` | Arithmetic over properties/literals | `{{ __calc(PaidAmount - Customer.CreditLimit):N2 }}` |
17+
18+
## Aggregates
19+
20+
Example model shape:
21+
22+
```csharp
23+
Customer.Payments = [
24+
{ Amount = 1000 },
25+
{ Amount = 2000 },
26+
{ Amount = 1500 }
27+
];
28+
```
29+
30+
Examples:
31+
32+
```text
33+
{{ __sum(Customer.Payments.Amount) }} -> 4500
34+
{{ __avg(Customer.Payments.Amount):N2 }} -> 1,500.00
35+
{{ __count(Customer.Payments.Amount) }} -> 3
36+
{{ __min(Customer.Payments.Amount) }} -> 1000
37+
{{ __max(Customer.Payments.Amount) }} -> 2000
38+
```
39+
40+
## Arithmetic with `calc`
41+
42+
Operators:
43+
44+
- `+`
45+
- `-`
46+
- `*`
47+
- `/`
48+
- Parentheses `(...)`
49+
50+
Examples:
51+
52+
```text
53+
{{ __calc(PaidAmount - Customer.CreditLimit):N2 }}
54+
{{ __calc((Subtotal + Tax) * 0.5):N2 }}
55+
{{ __calc(Quantity * UnitPrice) }}
56+
```
57+
58+
## Works Inside Loops
59+
60+
```text
61+
{{ #foreach(Items) }}
62+
[{{ Name }}={{ __calc(Quantity * UnitPrice):N2 }}]
63+
{{ #endforeach }}
64+
```
65+
66+
## Behavior Rules
67+
68+
| Scenario | Result |
69+
| --- | --- |
70+
| Valid numeric expression/path | Numeric output |
71+
| Null source/property in math path | `0` |
72+
| Unknown path/property in expression | Empty |
73+
| Non-numeric data in numeric expression (e.g. string/date for `sum`) | Empty |
74+
| Invalid expression syntax | Empty |
75+
76+
Notes:
77+
78+
- These rules apply to `sum`, `avg`, `count`, `min`, `max`, and `calc`.
79+
- Standard placeholder behavior is different: unknown regular properties remain unresolved (`{{ UnknownProp }}`).
80+
81+
## Formatting Calculated Results
82+
83+
Calculation results can use normal format specifiers:
84+
85+
```text
86+
{{ __sum(Customer.Payments.Amount):N2 }}
87+
{{ __calc(PaidAmount - CreditLimit):#,##0 }}
88+
```
89+
90+
Or no format at all:
91+
92+
```text
93+
{{ __sum(Customer.Payments.Amount) }}
94+
{{ __calc(PaidAmount - CreditLimit) }}
95+
```

0 commit comments

Comments
 (0)