Skip to content

MismatchException when loading a manifest containing content with / in the item name #92

Description

@jallenyoung

Component:
Tableau.Migration.JsonConverters — SerializableContentLocation.VerifyDeseralization()

SDK Version:
6.0.0

Reproduction steps:

  1. Have a Tableau Server source with a content item (workbook or datasource) whose name contains a literal / character (e.g. a workbook named "ItemWith/SlashInName" inside project "Top-Level").
  2. Create a mapping function that maps the item to a new top-level project using ContentLocation.create(path_separator, path_segments)
  3. Run a migration. The SDK serializes and saves manifest.json successfully.
  4. On the next run, pass the saved manifest to MigrationManifestSerializer.LoadAsync() to resume the migration.

Expected behavior:
The manifest loads successfully.

Actual behavior:
A MismatchException is thrown:

Tableau.Migration.JsonConverters.Exceptions.MismatchException:
Path should be Top-Level/ItemWith/SlashInName
         but is Top-Level/ItemWith\/SlashInName.
  at Tableau.Migration.JsonConverters.SerializableObjects.SerializableContentLocation.VerifyDeseralization()
  at Tableau.Migration.JsonConverters.SerializableObjects.SerializableContentLocation.AsContentLocation()
  at Tableau.Migration.JsonConverters.SerializableObjects.SerializableContentReference.AsContentReferenceStub()
  at Tableau.Migration.JsonConverters.SerializableObjects.SerializableManifestEntry...get_Source()

Root cause analysis:
The manifest JSON serializer writes the / character inside item names as / — an optional but valid JSON escape sequence per the JSON specification (RFC 8259 §7). However, VerifyDeseralization() appears to reconstruct the expected path string from the content location segments (using unescaped /) and then compares it as a raw string against the value deserialized from JSON. Because the JSON parser returns the literal two-character sequence / rather than normalizing it to / before the comparison, the strings differ and the exception is thrown.

This is a round-trip bug: the serializer produces JSON the deserializer cannot accept, on the same machine, with no external changes to the manifest file.

Workaround:
Manually post-process manifest.json after each save, replacing all / occurrences with /. Since these are equivalent JSON representations of the same character, this is semantically safe but should not be required.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions