An import is a pipeline of distinct decisions
A useful import boundary separates parsing the representation, validating records, calculating a result, and formatting it for display. The sample turns a JSON array into ImportRow records, rejects a row with a blank name or negative amount, and otherwise sums decimal amounts. Pipeline stages make the ownership of each rule visible. They also expose the limits of the example: a short successful aggregate does not prove that every malformed input, missing member, duplicate record, or null element has already received a complete policy.
Verify the sum before reading the formatted string
The first row is named A with amount twelve point five, and the second is named B with amount four point two five. Both names are nonblank and both amounts are nonnegative, so Any finds no rejected row. The else branch sums them to sixteen point seven five and formats that decimal with two places and invariant culture. The expected output is 16.75. Checking the two contributions independently confirms that formatting is displaying the intended aggregate rather than concealing a row-selection or arithmetic mistake.
rows.Sum(row => row.Amount).ToString("0.00", CultureInfo.InvariantCulture)Validation is an all-record acceptance boundary
Any asks whether at least one row violates the displayed predicate. If such a row exists, the program prints invalid import instead of producing a total from a silently filtered subset. This is an all-record acceptance policy for the two stated rules. Another application might accept valid rows and report rejected ones separately, but that would be a different contract. Decide the policy before replacing Any with Where merely to obtain a number despite invalid source records.
Null fallback does not validate every shape
The null-coalescing fallback makes a null array result behave like an empty array, whose sum formats as zero. That may or may not be a truthful import policy. It does not protect against a null element inside a nonnull array; the displayed predicate dereferences each row. Missing member presence and duplicate identities are also not checked here. Treat these as explicit extension points, not as evidence that using a record, Any, and a null fallback automatically establishes a fully validated schema.
Practice: change one row’s acceptance
Keep the two original amounts and set one name to whitespace, predicting invalid import. Restore the name and make one amount negative, again predicting rejection rather than a partial total. Then use a zero amount and explain why it is accepted under the existing less-than-zero rule. These tests distinguish nonnegative from positive and show why a validation predicate’s exact comparison is part of the import contract, not an incidental coding detail that can change without revisiting expected behavior.
- Write the accepted-row policy and expected branch before calculating any revised total.
- Do not print a sum of only valid rows unless partial acceptance is an intentional documented redesign.
Practice: define the missing and empty cases
For an independent design exercise, test an empty array, a null root, and an array containing null. Decide which should be accepted, rejected, or represented by a distinct no-data result. Then require a nonblank name and an explicitly supplied amount for each record, considering how required presence differs from an amount that legitimately equals zero. Add a duplicate-name policy only if names are intended identifiers; otherwise repeated names may be valid observations. Make each choice explicit before modifying the pipeline.
- Keep syntax errors, record validation errors, and aggregate formatting errors distinguishable in diagnostics.
- Check that the final total includes every accepted row exactly once.
Let the final number carry a justified meaning
A trustworthy import result means the stated shape and domain rules passed and the aggregate was calculated over the agreed rows. The baseline’s sixteen point seven five follows directly from two exact decimal values, but it does not define an external rounding or accounting standard. The invariant format makes output reproducible in this browser runtime. When expanding the example, preserve the separation between representation, completeness, record validity, aggregation, and presentation so a plausible-looking total cannot silently hide rejected or missing information.
- Baseline arithmetic is twelve point five plus four point two five, producing
16.75. - Any selects the rejection branch if even one row violates the stated rules.
- A null-root fallback is a policy choice, not complete validation of array elements or required member presence.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
var rows = JsonSerializer.Deserialize<ImportRow[]>("""[{"Name":"A","Amount":12.5},{"Name":"B","Amount":4.25}]""") ?? Array.Empty<ImportRow>();
if (rows.Any(row => string.IsNullOrWhiteSpace(row.Name) || row.Amount < 0))
Console.WriteLine("invalid import");
else Console.WriteLine(rows.Sum(row => row.Amount).ToString("0.00", CultureInfo.InvariantCulture));
record ImportRow(string Name, decimal Amount);
Expected output
16.75
Choose whether an invalid row rejects the import before calculating totals.