diff --git a/.gitignore b/.gitignore index 65fe298..b41fd5a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ target/ # Legacy: jars the since-removed tools/fetch-tools.sh used to download here. .lib/ -# Throwaway signing keys (XML_Signature/generate-test-key.sh) — never commit +# Throwaway signing keys (XML_Signature/java/GenerateTestKey.java) — never commit # private keys, even demo ones. XML_Signature/keys/ diff --git a/Data_Binding_JSON/README.md b/Data_Binding_JSON/README.md index f92b500..4a3d9c8 100644 --- a/Data_Binding_JSON/README.md +++ b/Data_Binding_JSON/README.md @@ -9,7 +9,8 @@ Two recurring enterprise needs: exposing FundsXML to **JSON** APIs, and [`python/fundsxml_json.py`](python/fundsxml_json.py) — `to-json` / `from-json` / `roundtrip` (stdlib + lxml). Stable JSON shape -(`document` / `fund` / `shareClasses` / `positions` / `assets`); `from-json` +(`document` / `funds[]` / per fund `shareClasses[]` + `portfolios[]/positions[]` / +`assets[]`); `from-json` produces **XSD-valid** FundsXML 4.2.9. Lossless for the positions core, intentionally lossy for issuer / derivative / regulatory detail (same scope boundary as `Database_Integration/`). diff --git a/Data_Binding_JSON/java/NativeBinding.java b/Data_Binding_JSON/java/NativeBinding.java index dfe0325..0fa6ec0 100644 --- a/Data_Binding_JSON/java/NativeBinding.java +++ b/Data_Binding_JSON/java/NativeBinding.java @@ -47,19 +47,23 @@ public static void main(String[] args) throws Exception { } XPath xp = XPathFactory.newInstance().newXPath(); - String ccy = xp.evaluate("/FundsXML4/Funds/Fund/Currency", doc); + // Single-fund binding on purpose: this example binds the FIRST Fund + // (metadata and positions alike). For a multi-fund document use the + // Python fundsxml_json.py, which projects funds[] as a list. + String f = "/FundsXML4/Funds/Fund[1]"; + String ccy = xp.evaluate(f + "/Currency", doc); Fund fund = new Fund( - xp.evaluate("/FundsXML4/Funds/Fund/Identifiers/LEI", doc), - xp.evaluate("/FundsXML4/Funds/Fund/Names/OfficialName", doc), + xp.evaluate(f + "/Identifiers/LEI", doc), + xp.evaluate(f + "/Names/OfficialName", doc), ccy, Double.parseDouble(xp.evaluate( - "/FundsXML4/Funds/Fund/FundDynamicData/TotalAssetValues/" + f + "/FundDynamicData/TotalAssetValues/" + "TotalAssetValue/TotalNetAssetValue/Amount[@ccy='" + ccy + "']", doc)), ((Number) xp.evaluate( - "count(//Position)", doc, XPathConstants.NUMBER)).intValue()); + "count(" + f + "//Position)", doc, XPathConstants.NUMBER)).intValue()); - NodeList pn = (NodeList) xp.evaluate("//Positions/Position", doc, + NodeList pn = (NodeList) xp.evaluate(f + "//Positions/Position", doc, XPathConstants.NODESET); List positions = new ArrayList<>(); double sumPct = 0; diff --git a/Data_Binding_JSON/python/fundsxml_json.py b/Data_Binding_JSON/python/fundsxml_json.py index 043d5cf..8c5beef 100644 --- a/Data_Binding_JSON/python/fundsxml_json.py +++ b/Data_Binding_JSON/python/fundsxml_json.py @@ -240,7 +240,8 @@ def from_json(data: dict, out_path: str) -> None: def main() -> int: a = sys.argv[1:] if len(a) != 3: - print(__doc__, file=sys.stderr) + print("usage: fundsxml_json.py to-json|from-json|roundtrip ", + file=sys.stderr) return 2 mode, src, dst = a if mode == "to-json": diff --git a/Database_Integration/README.md b/Database_Integration/README.md index e3da356..7512699 100644 --- a/Database_Integration/README.md +++ b/Database_Integration/README.md @@ -8,9 +8,9 @@ FundsXML — so you can lift exactly the direction you need: | Language | Import (FundsXML → DB) | Export (DB → FundsXML) | DB driver | Verified | |----------|------------------------|------------------------|-----------|----------| -| Python | [`python/import_fundsxml.py`](python/import_fundsxml.py) | [`python/export_fundsxml.py`](python/export_fundsxml.py) | stdlib `sqlite3` | ✅ locally | -| Java | [`java/ImportFundsXml.java`](java/ImportFundsXml.java) | [`java/ExportFundsXml.java`](java/ExportFundsXml.java) | `sqlite-jdbc` (native `javax.xml`, no JAXB) | ✅ locally | -| JavaScript | [`javascript/import_fundsxml.mjs`](javascript/import_fundsxml.mjs) | [`javascript/export_fundsxml.mjs`](javascript/export_fundsxml.mjs) | `sql.js` (pure-WASM) | ✅ locally | +| Python | [`python/import_fundsxml.py`](python/import_fundsxml.py) | [`python/export_fundsxml.py`](python/export_fundsxml.py) | stdlib `sqlite3` | ✅ locally + CI | +| Java | [`java/ImportFundsXml.java`](java/ImportFundsXml.java) | [`java/ExportFundsXml.java`](java/ExportFundsXml.java) | `sqlite-jdbc` (native `javax.xml`, no JAXB) | ✅ locally + CI | +| JavaScript | [`javascript/import_fundsxml.mjs`](javascript/import_fundsxml.mjs) | [`javascript/export_fundsxml.mjs`](javascript/export_fundsxml.mjs) | `sql.js` (pure-WASM) | ✅ locally + CI | | C# / .NET | [`csharp/import/`](csharp/import/) | [`csharp/export/`](csharp/export/) | `Microsoft.Data.Sqlite` | ✅ locally + CI | Every program is **self-contained** (one file / one project, its own copy of @@ -63,6 +63,8 @@ FX=FundsXML_Files/4.2.9/positions/Multi-Fund_Positions.xml DOC=FUNDSXML_MULTI_1 # Python +# Python needs lxml: create the venv from the repo's pyproject.toml once +# (python -m venv .venv && . .venv/bin/activate && pip install -e .) python3 Database_Integration/python/import_fundsxml.py fx.db "$FX" python3 Database_Integration/python/export_fundsxml.py fx.db "$DOC" out.xml diff --git a/Database_Integration/csharp/export/ExportFundsXml.csproj b/Database_Integration/csharp/export/ExportFundsXml.csproj index 4ca9c9f..bd9ad08 100644 --- a/Database_Integration/csharp/export/ExportFundsXml.csproj +++ b/Database_Integration/csharp/export/ExportFundsXml.csproj @@ -1,7 +1,8 @@ + Run with "dotnet run" and the project switch pointing at this + directory, then the arguments fx.db id out.xml; the exact command line + is in the ExportFundsXml.cs header (XML comments cannot contain a double dash). --> Exe net8.0 diff --git a/Database_Integration/csharp/import/ImportFundsXml.csproj b/Database_Integration/csharp/import/ImportFundsXml.csproj index b53feca..ecd7253 100644 --- a/Database_Integration/csharp/import/ImportFundsXml.csproj +++ b/Database_Integration/csharp/import/ImportFundsXml.csproj @@ -1,8 +1,9 @@ + Run with "dotnet run" and the project switch pointing at this + directory, then the arguments fx.db file.xml; the exact command line is + in the ImportFundsXml.cs header (XML comments cannot contain a double dash). --> Exe net8.0 diff --git a/Database_Integration/java/ExportFundsXml.java b/Database_Integration/java/ExportFundsXml.java index 6b0d0ab..1a7a784 100644 --- a/Database_Integration/java/ExportFundsXml.java +++ b/Database_Integration/java/ExportFundsXml.java @@ -78,8 +78,12 @@ public static void main(String[] args) throws Exception { doc.appendChild(root); try (Connection c = DriverManager.getConnection("jdbc:sqlite:" + db)) { - ResultSet d = c.createStatement().executeQuery( - "SELECT * FROM document WHERE document_id='" + docId + "'"); + // Bind the id (never concatenate user input into SQL) — the same + // rule every other query in this file follows. + PreparedStatement dq = c.prepareStatement( + "SELECT * FROM document WHERE document_id = ?"); + dq.setString(1, docId); + ResultSet d = dq.executeQuery(); if (!d.next()) throw new RuntimeException("no document " + docId); Element cd = el(doc, root, "ControlData", null); diff --git a/Database_Integration/javascript/export_fundsxml.mjs b/Database_Integration/javascript/export_fundsxml.mjs index 1e60e88..90635c4 100644 --- a/Database_Integration/javascript/export_fundsxml.mjs +++ b/Database_Integration/javascript/export_fundsxml.mjs @@ -9,7 +9,7 @@ // RUN // node import_fundsxml.mjs fx.db some.xml // node export_fundsxml.mjs fx.db FUNDSXML_MULTI_1 out.xml -// node ../tools/xml_equiv.py # (python) prove some.xml == out.xml +// python3 ../tools/xml_equiv.py some.xml out.xml # (needs lxml, see pyproject.toml) prove some.xml == out.xml // // DEPENDENCIES sql.js (WASM SQLite) + @xmldom/xmldom (DOM serialization). // diff --git a/Database_Integration/javascript/package.json b/Database_Integration/javascript/package.json index 4a2037f..b7f8bdf 100644 --- a/Database_Integration/javascript/package.json +++ b/Database_Integration/javascript/package.json @@ -4,9 +4,10 @@ "private": true, "type": "module", "description": "Standalone FundsXML <-> SQLite round-trip example (Node.js).", - "//": "Pure-JS deps so it runs anywhere with no native build: sql.js is SQLite compiled to WebAssembly; @xmldom/xmldom + xpath give a namespace-free DOM. Run: npm install && node fundsxml_db.mjs roundtrip ", + "//": "Pure-JS deps so it runs anywhere with no native build: sql.js is SQLite compiled to WebAssembly; @xmldom/xmldom + xpath give a namespace-free DOM. Run: npm install && node import_fundsxml.mjs fx.db && node export_fundsxml.mjs fx.db ", "scripts": { - "roundtrip": "node fundsxml_db.mjs roundtrip" + "import": "node import_fundsxml.mjs", + "export": "node export_fundsxml.mjs" }, "dependencies": { "@xmldom/xmldom": "^0.9.8", diff --git a/FundsXML_Files/4.2.9/positions/README.md b/FundsXML_Files/4.2.9/positions/README.md index e3d88a5..b599e59 100644 --- a/FundsXML_Files/4.2.9/positions/README.md +++ b/FundsXML_Files/4.2.9/positions/README.md @@ -46,7 +46,7 @@ This directory contains a comprehensive FundsXML sample document demonstrating a ### Portfolio Summary - **Total Positions**: 21 -- **Asset Types**: 13 different types +- **Asset Types**: 12 different types - **Currencies**: EUR, USD ## Asset Types Demonstrated @@ -72,7 +72,7 @@ This sample file includes examples of all major FundsXML asset types: | ID | ISIN | Name | Type | Value (EUR) | % | |----|------|------|------|-------------|---| -| ID_001 | US0378331005 | Apple Inc. | EQ | 9,375,000 | 7.50% | +| ID_001 | US0378331005 | Apple Inc. | EQ | 9,375,000 | 8.33% | | ID_002 | NL0010273215 | ASML Holding N.V. | EQ | 8,125,000 | 6.50% | | ID_003 | DE0001102424 | Germany 1.70% 2032 | BO | 6,250,000 | 5.00% | | ID_004 | XS2444622110 | Siemens 0.75% 2030 | BO | 6,250,000 | 5.00% | @@ -145,7 +145,7 @@ illustrative; the XML file is authoritative (see Reconciliation note). 9375000 - 7.50 + 8.33 50000.00 diff --git a/FundsXML_Files/4.2.9/regulatory/README.md b/FundsXML_Files/4.2.9/regulatory/README.md index 089b598..4d5c445 100644 --- a/FundsXML_Files/4.2.9/regulatory/README.md +++ b/FundsXML_Files/4.2.9/regulatory/README.md @@ -11,7 +11,7 @@ ## Contents -`RegulatoryReportings/DirectReporting/EFTs/EFT` (European Feeder/Flow Template). +`RegulatoryReportings/DirectReporting/EFTs/EFT` (European Feedback Template, FinDatEx EFT V1.0). Deliberately chosen because it is the **most compact** of the regulatory FundsXML structures (EMT/EET/PRIIPS/TPT are considerably larger and will follow as their own examples in later phases). diff --git a/FundsXML_Files/4.2.9/signed/README.md b/FundsXML_Files/4.2.9/signed/README.md index e9e0017..2d1ac25 100644 --- a/FundsXML_Files/4.2.9/signed/README.md +++ b/FundsXML_Files/4.2.9/signed/README.md @@ -20,7 +20,9 @@ local `FundsXML.xsd` in any complete copy of the release). > ⚠️ **Placeholder:** `DigestValue` and `SignatureValue` are schema-valid base64 > strings but **not cryptographically verifiable**. Real signing and > verification (Apache Santuario / .NET `SignedXml` / `xmlsec1` / Python -> `signxml`) follows in **Phase 3** under `XML_Signature/`. +> `signxml`) lives in [`XML_Signature/`](../../../XML_Signature/README.md); +> note that this template uses inclusive C14N and a `ds:KeyName`, whereas the +> Java/.NET examples sign with exclusive C14N and embed the X.509 certificate. Algorithms used (enveloped signature): C14N 2001-03-15, RSA-SHA256, SHA-256. diff --git a/FundsXML_Files/README.md b/FundsXML_Files/README.md index 1de8565..c7c167c 100644 --- a/FundsXML_Files/README.md +++ b/FundsXML_Files/README.md @@ -146,7 +146,10 @@ XSD_Validation/cli/validate.sh \ # xmllint --noout --schema /tmp/FundsXML.xsd ``` -### Validate with Saxon +### Validate with Saxon (EE only) + +Schema validation is a Saxon-**EE** feature (Saxon-HE, the edition used by the +examples in this repository, does not validate against XSD): ```bash saxon -s:Mixed-Fund_Positions.xml -xsd:FundsXML.xsd -o:validation-report.xml @@ -219,11 +222,12 @@ schema URL it was validated against. | Version | Use case | File | Description | |---------|----------|------|-------------| -| [4.2.9](./4.2.9/positions/) | positions | `Mixed-Fund_Positions.xml` | Comprehensive, 21 diverse positions, 13 asset types | +| [4.2.9](./4.2.9/positions/) | positions | `Mixed-Fund_Positions.xml` | Comprehensive, 21 diverse positions, 12 asset types | +| [4.2.9](./4.2.9/positions/) | positions | `Multi-Fund_Positions.xml` | 3 funds, 6 positions; lossless round-trip fixture for `Database_Integration/` | | [4.2.9](./4.2.9/transactions/) | transactions | `Fund_Transactions.xml` | BUY/SELL/CASH, `AssetUniqueID` IDREF linking | | [4.2.9](./4.2.9/documents/) | documents | `Fund_Documents.xml` | Factsheet (URL) + PRIIPS-KID (embedded base64) | | [4.2.9](./4.2.9/regulatory/) | regulatory | `EFT_Regulatory.xml` | `RegulatoryReportings/DirectReporting/EFTs` | -| [4.2.9](./4.2.9/signed/) | signed | `Signed_Fund_Skeleton.xml` | Enveloped `ds:Signature` (placeholder, Phase 3) | +| [4.2.9](./4.2.9/signed/) | signed | `Signed_Fund_Skeleton.xml` | Enveloped `ds:Signature` skeleton (schema-valid, not verifiable — real signing in `XML_Signature/`) | | [4.1.0](./4.1.0/positions/) | positions | `Equity-Fund_Positions.xml` | Compact equity fund, older valid version | | [4.0.0](./4.0.0/positions/) | positions | `Equity-Fund_Positions.xml` | Oldest release — **no `ControlData/Version`** | diff --git a/Large_File_Processing/README.md b/Large_File_Processing/README.md index b00b4e6..01ffa25 100644 --- a/Large_File_Processing/README.md +++ b/Large_File_Processing/README.md @@ -4,8 +4,12 @@ Enterprise FundsXML feeds can be hundreds of MB. Loading them into a DOM blows memory; these examples process them **streaming, at constant memory** — -verified: ~16 MiB RSS (Python) / ~2 MiB heap (Java, `-Xmx64m`) for a 20 000- -position file, independent of file size. +verified: ~16 MiB RSS (Python) at 30 000 **and** 200 000 positions, and the +Java StAX aggregator completes a 200 000-position file under `-Xmx16m`. (The +heap figure the Java program prints when run through `./mvnw … exec:java` +includes Maven's own in-process JVM; run the compiled class directly with +`java -Xmx16m -cp Large_File_Processing/java/target/classes StreamAggregate` +to see the parser alone.) | Tool | What | Status | |------|------|--------| diff --git a/Schematron_DataQuality_Checks/Basic_Checks/README.md b/Schematron_DataQuality_Checks/Basic_Checks/README.md index 7b4cba0..8f0d246 100644 --- a/Schematron_DataQuality_Checks/Basic_Checks/README.md +++ b/Schematron_DataQuality_Checks/Basic_Checks/README.md @@ -129,14 +129,29 @@ Validates currency codes and Amount elements. ## Running the Validation -### Prerequisites +### Quick Start (recommended: repo runners, nothing to install) -1. Install Saxon-HE (see [parent README](../README.md) for installation) -2. Download SchXslt for Schematron compilation +The runners in [`invocation/`](invocation/) pull SchXslt (which bundles its +own Saxon) from Maven Central via the committed Maven Wrapper; from the repo +root: -### Quick Start +```bash +./mvnw -q -pl Schematron_DataQuality_Checks/Basic_Checks/invocation compile exec:java \ + -Dexec.args="Schematron_DataQuality_Checks/Basic_Checks/basic_checks.sch \ + FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml" # exit 0 = no ERROR +``` + +See [`invocation/README.md`](invocation/README.md) for the Python +(`saxonche`), .NET and `svrl-summary.py` variants and the exit-code contract. + +### Manual pipeline (hand-installed Saxon + SchXslt) + +#### Prerequisites + +1. Saxon-HE jar (download from ) +2. SchXslt for Schematron compilation -#### Using Saxon + SchXslt (Recommended) +#### Using Saxon + SchXslt ```bash # Download SchXslt @@ -145,7 +160,7 @@ git clone https://github.com/schxslt/schxslt.git # Validate java -jar saxon-he.jar \ -xsl:schxslt/2.0/pipeline-for-svrl.xsl \ - -s:../../FundsXML_Files/4.2.9/Mixed-Fund_Positions.xml \ + -s:../../FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml \ sch.file=basic_checks.sch \ -o:validation_report.xml ``` @@ -161,7 +176,7 @@ java -jar saxon-he.jar \ # Step 2: Run validation java -jar saxon-he.jar \ - -s:../../FundsXML_Files/4.2.9/Mixed-Fund_Positions.xml \ + -s:../../FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml \ -xsl:basic_checks_compiled.xsl \ -o:validation_report.xml ``` @@ -196,7 +211,7 @@ Write-Host "Errors: $errors, Warnings: $warnings" #!/bin/bash SAXON_JAR="${SAXON_JAR:-/opt/saxon/saxon-he.jar}" SCHXSLT="${SCHXSLT:-/opt/schxslt/2.0/pipeline-for-svrl.xsl}" -INPUT="${1:-../../FundsXML_Files/4.2.9/Mixed-Fund_Positions.xml}" +INPUT="${1:-../../FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml}" OUTPUT="${2:-validation_report.xml}" java -jar "$SAXON_JAR" \ @@ -420,4 +435,4 @@ Percentage Sum = 100% - [ISO Schematron Specification](http://schematron.com) - [SchXslt Documentation](https://github.com/schxslt/schxslt) - [FundsXML Schema](https://github.com/fundsxml/schema) -- [Parent Schematron README](../README.md) +- [Invocation README (runners per stack)](invocation/README.md) diff --git a/Schematron_DataQuality_Checks/Basic_Checks/invocation/README.md b/Schematron_DataQuality_Checks/Basic_Checks/invocation/README.md index 8298b7a..a409a64 100644 --- a/Schematron_DataQuality_Checks/Basic_Checks/invocation/README.md +++ b/Schematron_DataQuality_Checks/Basic_Checks/invocation/README.md @@ -39,7 +39,7 @@ exits 1 on any failed-assert (including warnings). |-------|------|----------------------| | Java (native) | [`SchematronValidate.java`](SchematronValidate.java) | ✅ verified (SchXslt Java API, via Maven Wrapper) | | Python | [`validate_schematron.py`](validate_schematron.py) | saxonche via repo venv (`pip install -e .`); SchXslt jar via `$FUNDSXML_SCHXSLT_JAR` or Maven local repo — reference variant | -| .NET/C# | [`SchematronValidate.cs`](SchematronValidate.cs) | SaxonHE via NuGet (`dotnet build`); SchXslt jar via `$FUNDSXML_SCHXSLT_JAR` or Maven local repo — reference variant | +| .NET/C# | [`SchematronValidate.cs`](SchematronValidate.cs) | reference variant, **does not restore as-is**: Saxonica publishes its .NET packages (`SaxonHE12Net*`) as dotnet *tools*, not libraries (`NU1212`); pair the code with a Saxon .NET library build of your own. SchXslt jar via `$FUNDSXML_SCHXSLT_JAR` or Maven local repo | | shared | [`svrl-summary.py`](svrl-summary.py) | ✅ classifier used by all + CI | The Java example runs standalone and cross-platform via the committed Maven diff --git a/Schematron_DataQuality_Checks/Basic_Checks/invocation/SchematronValidate.csproj b/Schematron_DataQuality_Checks/Basic_Checks/invocation/SchematronValidate.csproj index 026a1d6..3c85a54 100644 --- a/Schematron_DataQuality_Checks/Basic_Checks/invocation/SchematronValidate.csproj +++ b/Schematron_DataQuality_Checks/Basic_Checks/invocation/SchematronValidate.csproj @@ -1,8 +1,11 @@ + repo (no NuGet SchXslt). Run from the repo root with "dotnet run", the + project switch pointing at this directory, then + Schematron_DataQuality_Checks/Basic_Checks/basic_checks.sch + [fail-on error|any]; see the SchematronValidate.cs header for the exact + command line (XML comments cannot contain a double dash). --> Exe net8.0 diff --git a/Schematron_DataQuality_Checks/Basic_Checks/invocation/pom.xml b/Schematron_DataQuality_Checks/Basic_Checks/invocation/pom.xml index 6ed1b72..0d485f5 100644 --- a/Schematron_DataQuality_Checks/Basic_Checks/invocation/pom.xml +++ b/Schematron_DataQuality_Checks/Basic_Checks/invocation/pom.xml @@ -12,7 +12,7 @@ Run from the repo root: ./mvnw -q -pl Schematron_DataQuality_Checks/Basic_Checks/invocation \ compile exec:java \ - -Dexec.args="../basic_checks.sch FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml" + -Dexec.args="Schematron_DataQuality_Checks/Basic_Checks/basic_checks.sch FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml" --> **Note on `xmlsec1`:** it signs an *existing* `ds:Signature` template, so it > pairs naturally with the committed signed skeleton; the Java/.NET/Python -> examples instead build and append the `ds:Signature` themselves. +> examples instead build and append the `ds:Signature` themselves. Because +> the skeleton's template uses **inclusive** C14N (`REC-xml-c14n-20010315`), +> only the enveloped transform and a `ds:KeyName` in `KeyInfo`, the xmlsec1 +> output does **not** follow the exclusive-C14N / embedded-X509 profile above: +> Java and .NET can still verify it against the pinned certificate, but not +> from the embedded `KeyInfo` (which carries no key). A real signed file is **not committed** — the signature is bound to the throwaway key, which is regenerated per run. CI signs → verifies as a roundtrip. diff --git a/XML_Signature/dotnet/SignVerify.cs b/XML_Signature/dotnet/SignVerify.cs index 44f168b..6134a64 100644 --- a/XML_Signature/dotnet/SignVerify.cs +++ b/XML_Signature/dotnet/SignVerify.cs @@ -4,7 +4,8 @@ // dotnet run --project XML_Signature/dotnet -- verify signed.xml [cert.pem] // Exit: 0 ok, 1 invalid, 2 setup error. // -// Reference implementation — not executed in the dev environment (no .NET SDK). +// Verified with .NET SDK 8: signs, verifies, detects tampering, and +// cross-verifies with the Java (Santuario) output in both directions. // Same profile as the Apache Santuario (Java) example: RSA-SHA256, exclusive // C14N, enveloped, signer cert embedded in KeyInfo, so files cross-verify. // Keys: the Java GenerateTestKey (./mvnw -pl XML_Signature/java compile diff --git a/XML_Signature/dotnet/SignVerify.csproj b/XML_Signature/dotnet/SignVerify.csproj index 8eba658..e4fcd2f 100644 --- a/XML_Signature/dotnet/SignVerify.csproj +++ b/XML_Signature/dotnet/SignVerify.csproj @@ -1,7 +1,9 @@ - + Exe net8.0 diff --git a/XML_Signature/java/VerifyFundsXml.java b/XML_Signature/java/VerifyFundsXml.java index fbf4950..e782095 100644 --- a/XML_Signature/java/VerifyFundsXml.java +++ b/XML_Signature/java/VerifyFundsXml.java @@ -65,6 +65,16 @@ public static void main(String[] args) throws Exception { signature.getKeyInfo().getX509Certificate(); PublicKey pk = embedded != null ? embedded.getPublicKey() : signature.getKeyInfo().getPublicKey(); + // A KeyInfo without X509Data/KeyValue (e.g. the committed skeleton, + // which only carries ds:KeyName) yields no key at all; Santuario + // would throw "Didn't get a key". Report it as INVALID (exit 1) + // rather than crashing with a stack trace. + if (pk == null) { + System.out.println("INVALID: KeyInfo carries no usable key " + + "(no X509Data / KeyValue); pass a certificate to verify " + + "against"); + System.exit(1); + } ok = signature.checkSignatureValue(pk); System.out.println("verifying against KeyInfo-embedded key" + (embedded != null ? " (cert: " diff --git a/XML_Signature/python/sign_verify_signxml.py b/XML_Signature/python/sign_verify_signxml.py index 09da046..e40b1b9 100644 --- a/XML_Signature/python/sign_verify_signxml.py +++ b/XML_Signature/python/sign_verify_signxml.py @@ -10,11 +10,12 @@ Reference implementation (signxml not installed in the dev environment): pip install signxml Keys: the Java GenerateTestKey (./mvnw -pl XML_Signature/java compile -exec:java -Dexec.mainClass=GenerateTestKey) writes test-signing.p12 + -test-signing-cert.pem to XML_Signature/keys/; the PEM *private* key this -signxml path also needs is produced when the Python signature stack is -migrated to its build system. RSA-SHA256, exclusive C14N, enveloped — same profile as -the Apache Santuario (Java) example, so files cross-verify between stacks. +exec:java -Dexec.mainClass=GenerateTestKey) writes test-signing.p12, +test-signing-cert.pem and the PKCS#8 private key test-signing-key.pem (the +one this script needs) to XML_Signature/keys/. signxml is an optional extra +of the repo's pyproject.toml: pip install -e ".[signature]". +RSA-SHA256, exclusive C14N, enveloped — same profile as the Apache Santuario +(Java) example, so files cross-verify between stacks. """ import sys from pathlib import Path diff --git a/XQuery_Examples/top-holdings.xq b/XQuery_Examples/top-holdings.xq index 5d4abd5..bc25b70 100644 --- a/XQuery_Examples/top-holdings.xq +++ b/XQuery_Examples/top-holdings.xq @@ -5,7 +5,7 @@ resolved from AssetMasterData (Position<->Asset joined by UniqueID). External parameter: $n (number of holdings, default 10) - Saxon CLI: -param:n=5 + Saxon CLI: n=5 (XQuery takes bare name=value; the -param form is XSLT-only) s9api: qe.setExternalVariable(new QName("n"), new XdmAtomicValue(5)) FundsXML 4.x has no XML namespace — query bare element names. diff --git a/XSD_Validation/dotnet/XsdValidate.csproj b/XSD_Validation/dotnet/XsdValidate.csproj index c55f9a1..d1c4480 100644 --- a/XSD_Validation/dotnet/XsdValidate.csproj +++ b/XSD_Validation/dotnet/XsdValidate.csproj @@ -1,7 +1,8 @@ - + Exe net8.0 diff --git a/XSLT_DataQuality_Checks/Basic_Checks/README.md b/XSLT_DataQuality_Checks/Basic_Checks/README.md index 6860952..c8f4134 100644 --- a/XSLT_DataQuality_Checks/Basic_Checks/README.md +++ b/XSLT_DataQuality_Checks/Basic_Checks/README.md @@ -9,7 +9,7 @@ This directory contains an XSLT 2.0 stylesheet that transforms FundsXML document | **File** | `basic_checks.xslt` | | **XSLT Version** | 2.0 | | **Output Format** | HTML | -| **Check Sections** | 5 main validation areas | +| **Check Sections** | 6 main validation areas | | **Purpose** | Generate readable data quality reports | ## Requirements @@ -158,7 +158,7 @@ saxon -s:input.xml -xsl:basic_checks.xslt -o:report.html # Full path example java -jar /opt/saxon/saxon-he.jar \ - -s:../../FundsXML_Files/4.2.9/Mixed-Fund_Positions.xml \ + -s:../../FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml \ -xsl:basic_checks.xslt \ -o:dq_report.html @@ -188,7 +188,7 @@ Start-Process $OUTPUT ```bash #!/bin/bash -INPUT="${1:-../../FundsXML_Files/4.2.9/Mixed-Fund_Positions.xml}" +INPUT="${1:-../../FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml}" OUTPUT="${2:-dq_report.html}" saxon -s:"$INPUT" -xsl:basic_checks.xslt -o:"$OUTPUT" @@ -199,7 +199,7 @@ open "$OUTPUT" ```bash #!/bin/bash -INPUT="${1:-../../FundsXML_Files/4.2.9/Mixed-Fund_Positions.xml}" +INPUT="${1:-../../FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml}" OUTPUT="${2:-dq_report.html}" SAXON_JAR="${SAXON_JAR:-/opt/saxon/saxon-he.jar}" diff --git a/XSLT_DataQuality_Checks/Custom_Internal_Checks/custom_internal_checks.xslt b/XSLT_DataQuality_Checks/Custom_Internal_Checks/custom_internal_checks.xslt index 31da497..c522254 100644 --- a/XSLT_DataQuality_Checks/Custom_Internal_Checks/custom_internal_checks.xslt +++ b/XSLT_DataQuality_Checks/Custom_Internal_Checks/custom_internal_checks.xslt @@ -26,7 +26,11 @@ - + + + @@ -93,7 +97,7 @@

R3 — Concentration limit (max % per position)

+ select="//Position[number(TotalPercentage) gt $limitPct]"/>

PASS — no position exceeds the concentration limit.

diff --git a/XSLT_DataQuality_Checks/README.md b/XSLT_DataQuality_Checks/README.md index da018bc..4efbd61 100644 --- a/XSLT_DataQuality_Checks/README.md +++ b/XSLT_DataQuality_Checks/README.md @@ -20,6 +20,18 @@ XSLT (Extensible Stylesheet Language Transformations) is a W3C standard for tran |------|--------------|-------------| | `Basic_Checks/basic_checks.xslt` | 2.0 | Core validation checks, HTML output | | `Enhanced_Check/FundsXML_CompleteDQReport_HTML.xsl` | 1.0 | Comprehensive dashboard report | +| `Custom_Internal_Checks/custom_internal_checks.xslt` | 2.0 | Parameterised house rules (asset-type whitelist, ID convention, concentration limit, OTC counterparty LEI) | + +**Running the XSLT 2.0 stylesheets without installing anything:** the Saxon-HE +runner in `XSLT_Transformations/invocation/` (Maven Wrapper, deps from Maven +Central) and its Python twin (`run_transform.py`, `saxonche`) take +` [name=value…]`, e.g. from the repo root: + +```bash +./mvnw -q -pl XSLT_Transformations/invocation compile exec:java \ + -Dexec.args="XSLT_DataQuality_Checks/Basic_Checks/basic_checks.xslt \ + FundsXML_Files/4.2.9/positions/Mixed-Fund_Positions.xml report.html" +``` **Compatibility Notes:** - XSLT 1.0 files work with any processor (maximum compatibility) diff --git a/pyproject.toml b/pyproject.toml index ce4ac5e..bda0b44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,11 @@ dependencies = [ "saxonche>=12.4", ] +[project.optional-dependencies] +# XML_Signature/python/sign_verify_signxml.py only (the Java Santuario +# example is the verified path). Install with: pip install -e ".[signature]" +signature = ["signxml>=3.2"] + [tool.setuptools] # Dependency-only install: nothing in the repo is packaged or importable, so # declare an empty module list (this also disables setuptools auto-discovery,