|
1 | 1 | # ObjectSemantics.NET |
2 | 2 | [](https://app.fossa.com/projects/git%2Bgithub.com%2Fswagfin%2FObjectSemantics.NET?ref=badge_shield) |
3 | 3 |
|
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 |
20 | 26 | Install from [NuGet](https://www.nuget.org/packages/ObjectSemantics.NET): |
21 | 27 |
|
22 | | -```bash |
| 28 | +```powershell |
23 | 29 | Install-Package ObjectSemantics.NET |
24 | 30 | ``` |
25 | 31 |
|
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 |
52 | 33 | ```csharp |
53 | | -Person person = new Person |
54 | | -{ |
55 | | - Name = "Jane Doe" |
56 | | -}; |
| 34 | +using ObjectSemantics.NET; |
57 | 35 |
|
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 |
67 | 39 | ``` |
68 | | ---- |
69 | | - |
70 | | -### Example 3: Mapping Enumerable Collections (Looping) |
71 | 40 |
|
| 41 | +## Template Examples |
| 42 | +### Nested mapping |
72 | 43 | ```csharp |
73 | | -Person person = new Person |
| 44 | +var payment = new CustomerPayment |
74 | 45 | { |
75 | | - MyCars = new List<Car> |
| 46 | + Amount = 100000000, |
| 47 | + Customer = new Customer |
76 | 48 | { |
77 | | - new Car { Make = "BMW", Year = 2023 }, |
78 | | - new Car { Make = "Rolls-Royce", Year = 2020 } |
| 49 | + CompanyName = "CRUDSOFT TECHNOLOGIES" |
79 | 50 | } |
80 | 51 | }; |
81 | 52 |
|
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 |
90 | 55 | ``` |
91 | 56 |
|
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 |
101 | 58 | ```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 }}"; |
117 | 60 | ``` |
118 | 61 |
|
119 | | -**Output:** |
120 | | -``` |
121 | | -Adult |
122 | | -``` |
123 | | ---- |
124 | | -### Example 5: Number Formatting Support |
125 | | - |
| 62 | +### Condition |
126 | 63 | ```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 }}"; |
138 | 65 | ``` |
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: |
146 | 66 |
|
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 | | -[](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) |
0 commit comments