Problem/Motivation

On a D7 site that's being migrated to D8, everything works fine except that a few nodes that have faulty JSON in their body fields because somehow some newlines characters were added to e.g. an embedded description.

The following media_wysiwyg token in paragraph nid [nid] does not have valid JSON:

Steps to reproduce

Have a D7 site with contents that have a body, has Media WYSIWYG enabled, and has one node with a file embedded via WYSIWYG that contains an errant linebreak in its JSON string.
Run Media Migration, it will output the error above.

Proposed resolution

Any suggestions for handling this?

Remaining tasks

?

User interface changes

?

API changes

?

Data model changes

?

Comments

DamienMcKenna created an issue. See original summary.

damienmckenna’s picture

Disclaimer: yes, going back and fixing the source data is probably the best option.

trobey’s picture

Title: How to deal with borken embedded JSON? » How to deal with broken embedded JSON?

I tracked down the faulty JSON to the node having " instead of the double quote. This was hard to figure out because it is displayed as a double quote so it looks correct. The newlines were more obvious but did not cause a problem. I suppose it might be possible to have the error message be more specific but I am not sure it is worthwhile.

trobey’s picture

Below is sample code to correct this problem. If you know the node IDs then you can use $nid = $row->getSourceProperty('nid'); to restrict the fix to just those nodes.

function hook_migrate_MIGRATION_ID_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
  $body = $row->getSourceProperty('field_text_body');
  $body[0]['value'] = preg_replace('/"/', '"', $body[0]['value']);
  $row->setSourceProperty('field_text_body', $body);
}
damienmckenna’s picture

Thanks @trobey.

Would it be worth adding this as documentation? What do you think, maintainers?

wim leers’s picture

Would it be worth adding this as documentation? What do you think, maintainers?

IMHO it'd be better to incorporate automatic fixes like #4 for "likely JSON malformations".

Based on #4, it looks like we're dealing with a HTML encoding problem? Which actually makes me wonder if there is a deeper rooted problem here.

It'd be really helpful if both of you could share examples of the input that is crashing, because that may or may not confirm my hypothesis 🤓🤞

wim leers’s picture

To clarify:

IMHO it'd be better to […]

… because automatically fixing what we can is better than expecting users futzing around with migration definition tweaks!

wim leers’s picture

Title: How to deal with broken embedded JSON? » Automatically fix broken embedded JSON
Category: Support request » Feature request
Status: Active » Postponed (maintainer needs more info)

Let's assume we move forward here with what I wrote in #6 — which means this becomes a feature request.

OTOH, to then do this, we really should have some concrete examples. Can you provide some, @DamienMcKenna and @trobey? 🙏

huzooka’s picture

Especially, you could also add it to the process plugin's test here: https://git.drupalcode.org/project/media_migration/-/blob/2fe44b9b32/tes...

damienmckenna’s picture

Status: Postponed (maintainer needs more info) » Needs review
StatusFileSize
new1.8 KB

Thanks for the pointer, huzooka.

I went back to the source data with the problem and mirrored a new item in providerTransformTest() based off it.

Status: Needs review » Needs work

The last submitted patch, 10: media_migration-n3197317-10.patch, failed testing. View results

wim leers’s picture

Perfect, thanks so much @DamienMcKenna! 👍

huzooka’s picture

BTW does this work in Drupal7 Media?

damienmckenna’s picture

No it doesn't display on D7, the page is left with some dirty JSON rendering on the page.

wim leers’s picture

That is reassuring on the one hand and disturbing on the other hand 😅 But hey, of course you want your new site to actually work as expected unlike the old one, so …

huzooka’s picture

Last time I touched this was when I fixed #3098833: Handle media_wysiwyg token JSON with line breaks, but that broken JSON (with linebreaks) could be "fixed" in Drupal 7 after an entity resave performed on Drupal UI.

wim leers’s picture

I just paired with @huzooka to figure whether we could safely automatically fix this kind of broken JSON. Good news 🤓🥳

<?php

$input = <<<INPUT
<p>Foo?</p>
[[{ &quot;fid&quot;:&quot;123456&quot;,&quot;view_mode&quot;:&quot;default&quot;,&quot;type&quot;:&quot;media&quot;,&quot;attributes&quot;:{ &quot;class&quot;:&quot;css-class1
css-class2 css-class3&quot; ,&quot;alt&quot;:&quot;Overridden alt attribute for embed&quot; ,&quot;title&quot;:&quot;Overridden title attribute for embed&quot; },
&quot;fields&quot;:{ &quot;format&quot;:&quot;default&quot; ,&quot;alt&quot;:&quot;Overridden alt attribute for embed&quot;,&quot;field_file_image_alt_text[und][0][value]&quot;:&quot;Media alt attribute&quot; ,&quot;title&quot;:&quot;Overridden title attribute for embed&quot;,&quot;field_file_image_title_text[und][0][value]&quot;:&quot;Media title attribute&quot;} }]]
<p>Bar baz!</p>
INPUT;

$pattern = '/\[\[\s*(?<tag_info>\{.+\})\s*\]\]/sU';
$matches = [];
var_dump(preg_match($pattern, $input, $matches));
var_dump($matches);

$tag_info = $matches['tag_info'];

var_dump('BEFORE');
var_dump(json_decode($tag_info));


function fix(string $parsed_potentially_broken_json): string {
  $r = str_replace("\n", " ", $parsed_potentially_broken_json);
  // This is what \Drupal\Component\Utility\Html::decodeEntities() does.
  return html_entity_decode($r, ENT_QUOTES, 'UTF-8');
}

var_dump('AFTER');
var_dump(json_decode(fix($tag_info)));

See it in action at https://3v4l.org/cD86J.

huzooka’s picture

Title: Automatically fix broken embedded JSON » Automatically fix HTML-encoded (broken) embedded JSON
Assigned: Unassigned » huzooka
huzooka’s picture

Added #10 and #17, and converted MediaWysiwygFilterTest to a unit test.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/src/Plugin/migrate/process/MediaWysiwygFilter.php
@@ -180,7 +181,12 @@ class MediaWysiwygFilter extends EmbedFilterBase implements ConfigurableInterfac
+          $tag_info = $decoder->decode(Html::decodeEntities($matches['tag_info']), JsonEncoder::FORMAT);

Why doesn't this need the

$r = str_replace("\n", " ", $parsed_potentially_broken_json);

anymore?

Ahhh … this pre-existing line:

      // Replace line breaks with a single space for valid JSON.
      $matches['tag_info'] = preg_replace('/\s+/', ' ', $matches['tag_info']);

👍


I don't see why we would not ship this hardening to media_migration users right away — the test coverage proves that it works. So … 🚢!

huzooka’s picture

Status: Reviewed & tested by the community » Fixed

  • huzooka committed 546a836 on 8.x-1.x
    Issue #3197317 by huzooka, DamienMcKenna, Wim Leers, trobey:...
damienmckenna’s picture

Excellent work, thank you!

Status: Fixed » Closed (fixed)

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