-
Notifications
You must be signed in to change notification settings - Fork 0
Examples
A tour of the patterns you'll most often need.
When JSON keys already match your C# properties:
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
}
var json = await httpClient.GetStringAsync("https://api.example.com/products");
var products = new List<Product>();
json.MapCollection(products, options =>
{
options.RootKey = "products";
});When JSON uses different names than your model:
// JSON: { "data": [{ "product_name": "Widget", "unit_price": 9.99 }] }
json.MapCollection(products, options =>
{
options.RootKey = "data";
options.Mappings = new Dictionary<string, string>
{
{ "Title", "product_name" },
{ "Price", "unit_price" }
};
});You only need to declare the differences. Properties whose names already match (case-insensitive) are picked up automatically.
Dot notation works for both RootKey and Mappings values:
{
"response": {
"data": {
"employees": [
{
"name": "Alice",
"work": {
"department": "Engineering",
"manager": { "email": "bob@example.com" }
}
}
]
}
}
}json.MapCollection(employees, options =>
{
options.RootKey = "response.data.employees";
options.Mappings = new Dictionary<string, string>
{
{ "Department", "work.department" },
{ "ManagerEmail", "work.manager.email" }
};
});Pre-populated lists (e.g. fetched from your DB) get matched by ItemKey:
var products = LoadProductsFromDatabase();
freshJson.MapCollection(products, options =>
{
options.RootKey = "products";
options.ItemKey = "Sku";
options.Mappings = new Dictionary<string, string>
{
{ "Price", "unit_price" },
{ "Description", "details.summary" }
};
});Each existing product is matched to a JSON row whose Sku matches, and only the mapped properties are updated. Unmatched products are left untouched. New JSON rows without a corresponding existing product are not added.
If your ItemKey is in Mappings, the mapper uses the mapped JSON path to find the value:
// JSON: [{ "product_name": "Widget", "price": 9.99 }]
// C# property: Title (no JSON key called "Title")
options.ItemKey = "Title"; // C# property name
options.Mappings = new()
{
{ "Title", "product_name" }, // JSON uses product_name
{ "Price", "price" }
};The lookup uses product_name from JSON but matches against the Title property in your existing items.
When the default ItemKey-is-empty rule doesn't fit your domain:
var profiles = LoadDraftProfiles();
json.MapCollection(profiles, options =>
{
options.RootKey = "employees";
options.ItemKey = "EmployeeId";
options.IsItemEmpty = item =>
{
var p = (EmployeeProfile)item;
// "Empty" = not yet finalized AND missing identifier
return !p.IsFinalized && string.IsNullOrEmpty(p.EmployeeId);
};
options.Mappings = new()
{
{ "EmployeeId", "id" },
{ "FullName", "displayName" }
};
});If every existing profile passes the predicate, the mapper enters Create Mode and rebuilds the list. If any profile fails it, the mapper enters Update Mode.
You only need Mappings entries for properties whose JSON names differ from your C# names. Other properties are mapped by direct name automatically.
public class Order
{
public int Id { get; set; } // matches "Id" / "id" automatically
public string Status { get; set; } // matches "Status" / "status" automatically
public decimal TotalAmount { get; set; } // needs mapping below
}
options.Mappings = new()
{
{ "TotalAmount", "total" } // only the one that differs
};When polling an API for updates, you can keep one list and refresh it on each call:
var products = new List<Product>();
while (running)
{
var json = await httpClient.GetStringAsync(apiUrl);
json.MapCollection(products, options =>
{
options.RootKey = "products";
options.ItemKey = "Sku";
options.Mappings = new() { { "Price", "unit_price" } };
});
// products list now has the latest prices for items it already knew about.
// New JSON rows are *not* added — handle that separately if you need it.
await Task.Delay(TimeSpan.FromMinutes(1));
}