OPXF 0.1.0
See the version list below for details.
dotnet add package OPXF --version 0.1.0
NuGet\Install-Package OPXF -Version 0.1.0
<PackageReference Include="OPXF" Version="0.1.0" />
<PackageVersion Include="OPXF" Version="0.1.0" />
<PackageReference Include="OPXF" />
paket add OPXF --version 0.1.0
#r "nuget: OPXF, 0.1.0"
#:package OPXF@0.1.0
#addin nuget:?package=OPXF&version=0.1.0
#tool nuget:?package=OPXF&version=0.1.0
opxf-dotnet
Strongly-typed .NET library for the Open Product Exchange Format (OPXF) — POCOs, System.Text.Json converters, and cross-file conformance validation.
Overview
OPXF is a PIM-agnostic transit format for moving product data from enterprise PIM systems (Akeneo, Inriver, Struct, Pimcore, Bluestone) into downstream consumers (e-commerce platforms, PDF engines, and other channels). This library provides the .NET foundation for working with OPXF payloads.
An OPXF exchange consists of two files:
| File | Purpose |
|---|---|
model.json |
The Definition Manifest — attribute types, localization rules, value lists, product types, category trees |
data.json |
The Product Payload — products, variants, supporting objects, and assets |
Installation
dotnet add package OPXF
Targets .NET 8 and .NET 9.
Usage
Deserialize
using OPXF;
var model = OpxfSerializer.DeserializeModel(File.ReadAllText("model.json"));
var data = OpxfSerializer.DeserializeData(File.ReadAllText("data.json"));
Validate
using OPXF.Validation;
var validator = new OPXFValidator();
var result = validator.Validate(model, data);
if (!result.IsValid)
{
foreach (var error in result.Errors)
Console.WriteLine(error); // [CONF-02] /data/products/0/productTypeId: unknown productTypeId 'apparel'
}
Serialize
string json = OpxfSerializer.SerializeData(data);
Stream-based I/O
For large payloads, use the stream overloads to avoid loading the full JSON into a string:
await using var modelStream = File.OpenRead("model.json");
await using var dataStream = File.OpenRead("data.json");
var model = await OpxfSerializer.DeserializeModelAsync(modelStream);
var data = await OpxfSerializer.DeserializeDataAsync(dataStream);
await using var outStream = File.Create("data.out.json");
await OpxfSerializer.SerializeDataAsync(data, outStream);
Work with attribute values
using OPXF.Data;
using OPXF.Model;
foreach (var product in data.Data.Products)
{
foreach (var (attrId, entry) in product.Attributes ?? [])
{
// Value shape depends on the model's localizable flag:
// localizable: false → string | double | bool | string[] | JsonElement
// localizable: true → IReadOnlyDictionary<string, object> keyed by BCP 47 locale
if (entry.Value is IReadOnlyDictionary<string, object> localeMap)
{
var enValue = localeMap["en-GB"];
}
else
{
var value = entry.Value;
}
}
}
Namespaces
| Namespace | Contents |
|---|---|
OPXF |
OpxfSerializer — entry point for serialization |
OPXF.Model |
OpxfModelFile, ModelDefinition, AttributeDefinition, AttributeType, ProductType, ObjectType, AssetType, CategoryType, Channel, AttributeGroup, SelectValue |
OPXF.Data |
OpxfDataFile, DataPayload, Product, Variant, OpxfObject, Asset, AttributeMap, AttributeValueEntry, CategoryGroup, CategoryNode |
OPXF.Validation |
OPXFValidator, ValidationResult, ValidationError |
Conformance validation
OPXFValidator.Validate() enforces the cross-file rules from docs/conformance.md — constraints that JSON Schema alone cannot express. Each ValidationError carries:
RuleId— the conformance rule code, e.g.CONF-02Message— human-readable description of the violationPath— RFC 6901 JSON Pointer to the offending element, e.g./data/products/0/productTypeId
OPXFValidator.ValidateModel() validates model internal integrity (CONF-31 through CONF-43) without a data file.
Validators collect all violations in a single pass — a payload returns all errors at once, not just the first.
Transfer mode
opxf.transferMode in the data file is advisory. When set to "incremental", referential existence rules (CONF-28, CONF-29, CONF-38) are relaxed — missing targets are the exporter's responsibility. When absent or unrecognised, "snapshot" semantics apply (strict enforcement).
Conformance rules implemented
Section 1 — Model/Data binding
| Rule | Description |
|---|---|
| CONF-01 | opxf.modelId in the data file must equal model.id in the model file |
Section 2 — Products
| Rule | Description |
|---|---|
| CONF-02 | product.productTypeId must reference a defined product type |
| CONF-03 | Every product.categories entry must reference a defined category node |
| CONF-04 | Product attribute ids must be declared in productType.productAttributes |
| CONF-05 | No two products may share the same id |
Section 3 — Variants
| Rule | Description |
|---|---|
| CONF-06 | Variant attribute ids must be declared in productType.variantAttributes; partial coverage allowed |
| CONF-07 | No two variants within the same product may share the same id |
Section 4 — Objects
| Rule | Description |
|---|---|
| CONF-09 | objectTypeId must reference a defined object type |
| CONF-10 | Object attribute ids must be declared in the resolved objectType.attributeDefinitions |
| CONF-11 | No two objects within the same type group may share the same id |
Section 5 — Assets
| Rule | Description |
|---|---|
| CONF-12 | assetTypeId must reference a defined asset type |
| CONF-13 | Asset attribute ids must be declared in the resolved assetType.attributeDefinitions |
| CONF-14 | No two assets within the same type group may share the same id |
Section 6 — Attribute value types
| Rule | Description |
|---|---|
| CONF-15 | Attributes with localizable: true must have value as a locale-keyed object |
| CONF-16 | Attributes with localizable: false must have value as a direct primitive |
| CONF-17 | Every locale key in any locale-keyed object (attribute values and all labels) must be in model.locales |
| CONF-18 | boolean values must be a JSON boolean — not the string "true" |
| CONF-19 | number values must be a JSON number |
| CONF-20 | text, textarea, and datetime values must be a string |
| CONF-21 | text-list values must be an array of strings |
| CONF-22 | select values must match a selectValue id; localizable select allows different ids per locale |
| CONF-23 | select-list values must be an array of valid selectValue ids |
| CONF-24 | object-link values must reference an existing object id in the correct type group |
| CONF-25 | object-link-list values must be an array of existing object ids in the correct type group |
| CONF-26 | asset-link values must reference an existing asset id in the correct type group |
| CONF-27 | asset-link-list values must be an array of existing asset ids in the correct type group |
| CONF-28 | product-link values must reference an existing product or variant id (snapshot mode only) |
| CONF-29 | product-link-list values must be an array of existing product or variant ids (snapshot mode only) |
| CONF-30 | json attributes accept any valid JSON value — no further enforcement |
Section 7 — Categories
| Rule | Description |
|---|---|
| CONF-44 | categoryTreeId values must be unique across all groups in data.categories |
| CONF-45 | A category node with attribute keys must belong to a group that declares a categoryTypeId |
| CONF-46 | Category node ids must be unique across the entire tree at all depths |
| CONF-47 | Category node attribute ids must be declared in the resolved categoryType.attributeDefinitions |
| CONF-49 | categoryTypeId on a category group must reference a defined categoryType |
| CONF-50 | categoryType ids must be unique within model.categoryTypes |
Section 8 — Model internal integrity
| Rule | Description |
|---|---|
| CONF-31 | productType.productAttributes ids must exist in productAttributeDefinitions |
| CONF-32 | productType.variantAttributes ids must exist in productAttributeDefinitions |
| CONF-33 | Attribute definition ids within each objectType must be unique |
| CONF-34 | Attribute definition ids within each assetType must be unique |
| CONF-35 | selectValue ids within each attribute must be unique |
| CONF-36 | attributeDefinition.objectTypeId must reference a defined objectType |
| CONF-37 | attributeDefinition.assetTypeId must reference a defined assetType |
| CONF-39 | channel.locales must be a subset of model.locales |
| CONF-40 | model.defaultLocale must be present in model.locales |
| CONF-41 | ids within each model-root array must be unique within that array |
| CONF-43 | attributeDefinition.groupId must reference a defined attributeGroup in the correct scope |
Section 9 — Entity id format
| Rule | Description |
|---|---|
| CONF-51 | All ids must match ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ (also enforced by JSON Schema) |
Section 10 — Cross-file references
| Rule | Description |
|---|---|
| CONF-38 | channel.categoryTreeIds must reference trees present in data.categories (snapshot mode only) |
Attribute types
The AttributeType class exposes string constants for all OPXF canonical types:
AttributeType.Text // "text"
AttributeType.TextList // "text-list"
AttributeType.Textarea // "textarea"
AttributeType.Number // "number"
AttributeType.Boolean // "boolean"
AttributeType.Datetime // "datetime"
AttributeType.Select // "select"
AttributeType.SelectList // "select-list"
AttributeType.AssetLink // "asset-link"
AttributeType.AssetLinkList // "asset-link-list"
AttributeType.ObjectLink // "object-link"
AttributeType.ObjectLinkList // "object-link-list"
AttributeType.ProductLink // "product-link"
AttributeType.ProductLinkList // "product-link-list"
AttributeType.Json // "json"
Related repositories
| Repository | Description |
|---|---|
| opxf/opxf-spec | The OPXF specification — JSON schemas and conformance rules |
| opxf/opxf-dotnet-akeneo | Akeneo → OPXF exporter built on this library |
| opxf/opxf-org | Source for opxf.org |
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.