I've got a migration set up whose source was apparently filled out using MS Word. The JSON source for the migration looks like this in places:

    "field_courses": "<p class=\"MsoNormal\" style=\"line-height: 14pt; background-position: initial initial; background-repeat: initial initial;\"><u><span style=\"font-size:10.0pt;&#10;font-family:\" new=\"\">Courses Taught (selected)<br />\r\n</span></u>...</p>"

When the migration completes and I open the resulting node, the field is rendered like this:

"before" screenshot

The field in question is allowed to use everything from Plain Text all the way to Full HTML. When I save the node manually, not making any changes, all the nonsense is stripped out and I'm left with relatively sane HTML when I check out the source of that field.

I thought maybe it would be as simple as loading the entity by its pathauto alias (eg. /people/[person_id]) and running $entity->save() on it as soon as it's been imported, but that's not removing the formatting. Any way that I can get it to display correctly without requiring a content manager to manually save 500+ nodes?

  • Not sure if this is still a way to approach it, but in the past (to Drupal 7) when I ran into this, I would add code and use DomDocument to strip and return cleaned HTML before migrating in. This ensured nothing "dirty" in that sense reached the database.
    – Kevin
    Aug 8, 2017 at 19:09
  • 1
    Here's a really old example (d7) I dug up just to show you.. I used this to strip all HTML and bad characters on migrate, but you could read some of the DOMDocument docs to look at how to manipulate elements, like p, and strip it of attributes you do not want. gist.github.com/kevinquillen/a9229f2f1b42048e512888fda7f2f9be -- See also: stackoverflow.com/questions/10619282/…
    – Kevin
    Aug 8, 2017 at 19:17
  • Interesting, I'll have to look at that. Main reason I didn't do that was because I do need to maintain some of the formatting, just not all the Word-centric stuff. Maybe there's a way to adapt that to do what I need.
    – tmountjr
    Aug 8, 2017 at 19:52
  • Yeah in the second link there, you can clear all attributes. You can also remove specific attributes too. Or kill things like MsoNormal class. Have a look at the docs, it is pretty flexible and one of the only ways to do this without nasty, sometimes unpredictable, regex. Also, dealing with Word, you can get invisible/bad characters coming in that are MS Word specific - I have had fatal errors with that before (hence all the checking in my gist to eliminate them).
    – Kevin
    Aug 8, 2017 at 19:57

1 Answer 1


Found a great way to do this automatically after comparing the Devel tab on a node before and after manually saving it. I realize this is a somewhat narrow use case, but I think it's pretty strong nonetheless.

  1. Create a CKEditor profile (I called it "Bio Cleaner", machine name bio_cleaner) and limit it to the tags and attributes you want. In my case, it's <a href hreflang> <ul type> <ol start type> <li> <br> <p>.
  2. For any "formatted" text field in your content types, add the new CKEditor profile as an allowed profile.
  3. In your migration, rather than just dumping the raw contents into a field name, set it up like so:

sample migration

(Sorry for the image - Stack Exchange apparently can't format yaml?)

Anyway, the secret sauce is to to set the /value key of the target field to the value pulled from the migration, and to set the /format key to the new format you created. Drupal will automagically clean up whatever tags aren't included in your profile when it saves the data.

NOTE: this relies on having the Allowed Formats module installed.

  • 1
    For anyone following this advice, I found that I also needed to install the HTML Purifier module drupal.org/project/htmlpurifier to strip extra MS-Word provided <xml><o:....></xml> elements. The built-in filter formats work fine for attributes, but the extra XML elements in some content were showing up as plain text in my results. Oct 21, 2019 at 19:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.