Mapping hierarchical and multi-path taxonomies
WordPress and WooCommerce use taxonomies to group posts and products — categories, tags, brands, attributes, custom taxonomies. SSI maps a CSV column to a taxonomy, but real-world CSVs do clever things: nested category paths, multi-category assignments, mixed separators. This article covers how to map all of them correctly.
Table of Contents
The basics — one term per row
Simplest case. CSV has one term per row in a column:
SKU,Name,Tag
ABC-001,Widget A,featured
ABC-002,Widget B,best-seller
Map Tag → product_tag. Each row gets one product tag.
Hierarchical taxonomies — the > separator
WooCommerce categories, custom hierarchical taxonomies (brand hierarchies, location hierarchies, etc.) live as trees: Electronics has children Phones, Laptops, Tablets; each child may have grandchildren. Your CSV expresses a row’s position in the tree by joining the path with a separator.
The conventional separator is >:
SKU,Name,Categories
ABC-001,iPhone Case,Electronics > Phones > Accessories
ABC-002,Wireless Mouse,Electronics > Laptops > Peripherals
Configure the taxonomy mapping for product_cat:
- Source:
Categories(the CSV column name) - Separator:
>
On import, SSI:
- Splits
Electronics > Phones > Accessoriesinto["Electronics", "Phones", "Accessories"](trimming whitespace around the>). - Walks the path, creating each level as a term if it doesn’t exist already, parented to the previous level.
- Assigns the product to every level of the path —
Electronics,Phones, ANDAccessories— so the product shows up on parent category pages too. (This is the new-import default; you can switch back to leaf-only. See Which category levels get assigned below.)
After the import, your category tree looks like:
Electronics ← iPhone Case is in here (default: all levels)
├── Phones ← iPhone Case is in here (default: all levels)
│ └── Accessories ← iPhone Case is in here (always — the leaf)
└── Laptops ← Wireless Mouse is in here (default: all levels)
└── Peripherals ← Wireless Mouse is in here (always — the leaf)
Spaces around the > are optional but recommended for human readability. SSI trims them. Both A > B > C and A>B>C work.
Which category levels get assigned
When you import a hierarchical path, SSI can attach the product to every level of that path or only to the deepest (leaf) term. This is controlled per-import by the Assign every category level (not just the deepest) option in Additional Options.
- ON — a product in
Men > T-Shirtsis assigned to BOTHMenANDT-Shirts(every level of the path). Parent category pages (/product-category/men/) list the product, breadcrumbs render the full path, and layered-navigation filters include parent categories. This matches WP All Import / WooCommerce expectations. - OFF — only the deepest (leaf) term is assigned (
T-Shirtsonly). This is the old behaviour; parent category pages won’t list the product.
New imports default this option ON. Imports created before this option existed (pre-2.55.9) keep their original leaf-only behaviour until you tick the box — so re-running an existing import won’t silently change which categories your products belong to. Switch it on (and re-run) when you want the all-levels behaviour on an older import.
The undocumented
SSI_ADD_ALL_TERMS_FROM_HIERARCHYPHP constant has been removed and no longer has any effect — use the Assign every category level option instead.
Choosing a different hierarchy separator
If your CSV uses something other than > (some feeds use /, others use ::), set the separator field accordingly. Pick a separator that doesn’t appear inside any term name. / is risky if you have terms like “and/or”; > is generally safe.
Multi-path assignments — the | separator
A single product often belongs to MULTIPLE categories. SSI uses | (pipe) to separate independent paths within one cell:
SKU,Name,Categories
ABC-001,iPhone Case,Electronics > Phones > Accessories | Sale > Black Friday
That single row places the product in BOTH:
Electronics > Phones > AccessoriesSale > Black Friday
Configure the taxonomy mapping:
- Source:
Categories - Separator:
> - Multi-separator:
|(this is the default, so usually nothing to do)
SSI splits on | FIRST (giving you the list of paths), then splits each path on > (giving you the hierarchy levels within that path). With the default Assign every category level option, the product ends up assigned to every level of every path; with that option off it’s assigned only to the leaf of each path. See Which category levels get assigned above.
Changing the multi-separator
If | clashes with your data (some product titles contain pipes), set the multi-separator to something else — comma, semicolon, your choice. Pick something that doesn’t appear inside any individual term name AND doesn’t appear inside any path.
Flat (non-hierarchical) taxonomies
Tags, product attributes (pa_color, pa_size), and most custom taxonomies are flat — no parent/child relationships. The separator is still useful here, but only for multi-value assignment:
SKU,Name,Tags
ABC-001,Widget A,featured|best-seller|gift-idea
Map Tags → product_tag with multi-separator |. The product gets all three tags.
For flat taxonomies you can leave the hierarchy separator (>) blank — there’s no hierarchy to express.
WooCommerce attributes — the pa_ prefix
Variable products use product attributes (color, size, material) that double as taxonomies. WooCommerce stores these under the pa_ prefix: pa_color, pa_size, pa_material.
SKU,Name,Type,Color,Size
TSHIRT-001,Cotton T-Shirt,variable,,
TSHIRT-001-RED-S,Cotton T-Shirt — Red / S,variation,Red,Small
TSHIRT-001-RED-M,Cotton T-Shirt — Red / M,variation,Red,Medium
TSHIRT-001-BLU-S,Cotton T-Shirt — Blue / S,variation,Blue,Small
Map Color → pa_color (set “Is variable” to on), Size → pa_size (set “Is variable” to on).
The Is variable flag tells SSI this attribute generates variations on the parent product. See the Importing variable products article for the full story.
Default term fallback
For hierarchical taxonomies that the CSV doesn’t map (e.g. you don’t have a product_brand column in your CSV but you’ve set up the product_brand taxonomy with a default term in WordPress), SSI’s upsert-relationships stage automatically assigns the default term to every imported product. Configure the default in Posts → Categories (or the equivalent admin UI for each taxonomy) by editing a term and ticking “Default term”.
Useful for “Generic” or “Uncategorised” brands you want every product to belong to without populating the CSV.
Common gotchas
Whitespace in term names
"Apparel > Hats " (trailing space inside the hierarchy) is a different term from "Apparel > Hats". SSI trims whitespace around the separator, but NOT inside the term name itself. Clean your CSV.
Mixing separators in one CSV
If some rows use > and others use /, you’ll get a mess. Pick one separator per taxonomy and stick to it. If the CSV is inconsistent, pre-process it to normalise.
Same hierarchy different name capitalisations
WordPress slug-deduplicates terms (Phones and phones both become the slug phones), so casing differences within ONE import collapse together. Across imports they should already be consistent — but if you find one CSV has Phones and another has phones, you may end up with a phones-2 slug. See Handling duplicate term slugs for the policy options.
Multi-path with empty paths
Categories: "A > B ||C > D" produces 3 paths after splitting on |: A > B, `,C > D`. The empty one is skipped silently. Watch for accidental double-pipes if your CSV is auto-generated.
Reserved term names
Don’t use 0, null, or parent as standalone term names — WordPress core has edge cases with these. Wrap them in something descriptive: Year 0, Null Category, Top-Level Parent.
Worked example — full WooCommerce category mapping
Source CSV:
SKU,Name,Categories,Tags,Color,Size
WIDGET-001,Cotton T-Shirt,Apparel > Tops > T-Shirts | Sale,unisex|cotton,Red|Blue|Green,S|M|L
WIDGET-002,Wool Hat,Apparel > Hats > Wool,winter,Black,One Size
Mappings:
| CSV column | Taxonomy | Hierarchy separator | Multi-separator |
|---|---|---|---|
Categories |
product_cat |
> |
| |
Tags |
product_tag |
(none) | | |
Color |
pa_color (variable) |
(none) | | |
Size |
pa_size (variable) |
(none) | | |
After import, WIDGET-001 belongs to both the Apparel > Tops > T-Shirts path and the Sale category. With the default Assign every category level option on, it’s attached to Apparel, Tops, T-Shirts AND Sale (so it shows on each of those category pages); with that option off it’s attached only to the leaf terms T-Shirts and Sale. It has tags unisex + cotton, and (because Color and Size are marked variable) generates 9 variations covering every Red/Blue/Green × S/M/L combination.
What’s next
- Importing variable products — when an attribute taxonomy drives variations.
- Handling duplicate term slugs — what to do when two CSV spellings collapse to the same WP slug.
- Choosing your unique identifier — the upstream prerequisite for any re-import.