Mapping hierarchical and multi-path taxonomies

June 5, 2026

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.

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 Tagproduct_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:

  1. Splits Electronics > Phones > Accessories into ["Electronics", "Phones", "Accessories"] (trimming whitespace around the >).
  2. Walks the path, creating each level as a term if it doesn’t exist already, parented to the previous level.
  3. Assigns the product to every level of the pathElectronics, Phones, AND Accessories — 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-Shirts is assigned to BOTH Men AND T-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-Shirts only). 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_HIERARCHY PHP 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 > Accessories
  • Sale > 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 Tagsproduct_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 Colorpa_color (set “Is variable” to on), Sizepa_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.
×
1/1