If a direct child of the item_selector has multiple values and those values are not strings, only a single value is preserved and passed to any process plugins.

From Drupal\migrate_plus\Plugin\migrate_plus\data_parser\Xml::fetchNextRow() ~ line 249

// If the SimpleXMLElement doesn't render to a string of any sort,
// and has children then return the whole object for the process
// plugin or other row manipulation.
if ($value->children() && !trim((string) $value)) {
  $this->currentItem[$field_name] = $value; // The culprit
}
else {
  $this->currentItem[$field_name][] = (string) $value;
}

Fix:

// If the SimpleXMLElement doesn't render to a string of any sort,
// and has children then return the whole object for the process
// plugin or other row manipulation.
if ($value->children() && !trim((string) $value)) {
  $this->currentItem[$field_name][] = $value; // Match the else, preserve all values.
}
else {
  $this->currentItem[$field_name][] = (string) $value;
}

Source XML:

<Categories>
  <Category>
      <Code>category_1</Code>
      <Name>Category 1</Name>
      <Items1>
        <SubItem>
          <Id>1</Id>
          <Name>Name 1</Name>
        </SubItem>
        <SubItem>
            <Id>2</Id>
          <Name>Name 2</Name>
        </SubItem>
      </Items1>
      <Items2>
          <SubItem>1</SubItem>
          <SubItem>2</SubItem>
      </Items2>
  </Category>
</Categories>

Migration template:

id: test_migration
migration_group: test
source:
  plugin: url
  urls: 'private://categories.xml'
  data_fetcher_plugin: file
  data_parser_plugin: xml
  item_selector: /Categories/Category

  ids:
    code:
      type: string

  fields:
    -
      name: code
      label: 'Code'
      selector: 'Code'
    -
      name: name
      label: 'Name'
      selector: 'Name'
    -
      name: sub_items1
      label: 'Sub items 1'
      selector: 'Items1/SubItem'
    -
      name: sub_items2
      label: 'Sub items 2'
      selector: 'Items2/SubItem'

process:
  vid:
    plugin: default_value
    default_value: test

  name: name

destination:
  plugin: entity:taxonomy_term

For fields which have one level selector (like sub_items2) it works fine. But for fields with several children and complex structure like sub_items1 it's returning only last element.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sonfd created an issue. See original summary.

sonfd’s picture

sonfd’s picture

Issue summary: View changes
sonfd’s picture

Status: Active » Needs review
hctom’s picture

DuaelFr’s picture

The same issue exists in the SimpleXml parser.

Raphael Apard’s picture

Status: Needs review » Reviewed & tested by the community

Works great for me

Matroskeen’s picture

Version: 8.x-4.x-dev » 8.x-5.x-dev
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

Thank you for the patch!
Do you think we can add a unit test to make sure it's not broken again?

As I see, we already have a bunch of XML tests. Let's push it back to "Needs Work" and see if we can add some :)

Thanks!

Matroskeen’s picture

By the way, we have some test examples in the related issue: #2986883: Xml data_parser reducing single-value results to scalar for SimpleXMLElements.

Matroskeen’s picture

Issue summary: View changes

I'm adding XML examples from duplicated issue #3102400: XML data parser holds only value for last child and transferring an issue credit.

Matroskeen’s picture

Version: 8.x-5.x-dev » 6.0.x-dev

  • Matroskeen committed 88c4f71 on 6.0.x
    Issue #3028162 by Matroskeen, DuaelFr, p-andrei: XML Parser - multiple...

  • Matroskeen committed a788489 on 8.x-5.x
    Issue #3028162 by Matroskeen, DuaelFr, p-andrei: XML Parser - multiple...
Matroskeen’s picture

Status: Needs work » Fixed
Issue tags: -Needs tests

Committed to 6.0.x and cherry-picked to 8.x-5.x.
Thanks!

Matroskeen’s picture

Adding a missing credit :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.