diff --git a/IO/CatalystConduit/vtkConduitArrayUtilities.h b/IO/CatalystConduit/vtkConduitArrayUtilities.h
index 4a05b04e3723025e4d10a25ed176a94d3c8acaa8..0e7b77b0aab0350a2b8abefe9cf148612bbca6f7 100644
--- a/IO/CatalystConduit/vtkConduitArrayUtilities.h
+++ b/IO/CatalystConduit/vtkConduitArrayUtilities.h
@@ -17,6 +17,7 @@
 #ifndef vtkConduitArrayUtilities_h
 #define vtkConduitArrayUtilities_h
 
+#include "vtkDeprecation.h"             // for VTK_DEPRECATED_IN_9_5_0
 #include "vtkIOCatalystConduitModule.h" // for exports
 #include "vtkObject.h"
 #include "vtkSmartPointer.h" // for vtkSmartPointer
@@ -51,7 +52,13 @@ public:
   /**
    * Returns a vtkDataArray from a conduit node in the conduit mcarray protocol
    * that is a conduit ghost array named ascent_ghosts.
+   *
+   * @deprecated: Instead of using this function, use the state/metadata/vtk_fields
+   * to define the attribute_type, values_to_replace and replacement_values instead for
+   * the ghost array.
    */
+  VTK_DEPRECATED_IN_9_5_0("This function is deprecated, because in the future "
+                          "state/metadata/vtk_fields will only be used.")
   static vtkSmartPointer<vtkDataArray> MCGhostArrayToVTKGhostArray(
     const conduit_node* mcarray, bool is_cell_data);
   ///@}
diff --git a/IO/CatalystConduit/vtkConduitToDataObject.cxx b/IO/CatalystConduit/vtkConduitToDataObject.cxx
index 0e8f2b45cd1456a5e7ade1ce188936d52c5ef3c8..30c55483cb65e041514730838865ea8ee34eb785 100644
--- a/IO/CatalystConduit/vtkConduitToDataObject.cxx
+++ b/IO/CatalystConduit/vtkConduitToDataObject.cxx
@@ -1,5 +1,9 @@
 // SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 // SPDX-License-Identifier: BSD-3-Clause
+
+// Added due to deprecated vtkConduitArrayUtilities::MCGhostArrayToVTKGhostArray
+#define VTK_DEPRECATION_LEVEL 0
+
 #include "vtkConduitToDataObject.h"
 
 #include "vtkAMRBox.h"
@@ -18,11 +22,14 @@
 #include "vtkParallelAMRUtilities.h"
 #include "vtkPartitionedDataSet.h"
 #include "vtkRectilinearGrid.h"
+#include "vtkSMPTools.h"
 #include "vtkStringArray.h"
 #include "vtkStructuredGrid.h"
 #include "vtkUniformGrid.h"
 #include "vtkUnstructuredGrid.h"
 
+#include "vtksys/SystemTools.hxx"
+
 #include <catalyst_conduit.hpp>
 #include <catalyst_conduit_blueprint.hpp>
 
@@ -33,6 +40,63 @@ namespace vtkConduitToDataObject
 {
 VTK_ABI_NAMESPACE_BEGIN
 
+//----------------------------------------------------------------------------
+struct FieldMetadata
+{
+  vtkSmartPointer<vtkDataArray> ValuesToReplace = nullptr;
+  vtkSmartPointer<vtkDataArray> ReplacementValues = nullptr;
+  std::string AttributeType;
+
+  static vtkDataSetAttributes::AttributeTypes GetDataSetAttributeType(
+    const std::string& otherAttributeTypeName)
+  {
+    for (int i = 0; i < vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES; ++i)
+    {
+      const std::string attributeTypeName = vtkDataSetAttributes::GetAttributeTypeAsString(i);
+      if (vtksys::SystemTools::UpperCase(otherAttributeTypeName) ==
+        vtksys::SystemTools::UpperCase(attributeTypeName))
+      {
+        return static_cast<vtkDataSetAttributes::AttributeTypes>(i);
+      }
+    }
+    return vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES;
+  }
+
+  static bool IsGhostsAttributeType(const std::string& otherAttributeTypeName)
+  {
+    return vtksys::SystemTools::UpperCase(otherAttributeTypeName) == "GHOSTS";
+  }
+};
+
+//----------------------------------------------------------------------------
+struct ReplaceValuesWorker
+{
+  template <typename Array1T, typename Array2T, typename Array3T>
+  void operator()(Array1T* valuesToReplace, Array2T* replacementValues, Array3T* array) const
+  {
+    const vtkIdType numValuesToReplace = valuesToReplace->GetNumberOfTuples();
+    auto valuesToReplaceRange = vtk::DataArrayValueRange(valuesToReplace);
+    auto replacementValuesRange = vtk::DataArrayValueRange(replacementValues);
+    auto arrayRange = vtk::DataArrayValueRange(array);
+
+    vtkSMPTools::For(0, array->GetNumberOfTuples(),
+      [&](vtkIdType begin, vtkIdType end)
+      {
+        for (vtkIdType inputIdx = begin; inputIdx < end; ++inputIdx)
+        {
+          for (vtkIdType repValueId = 0; repValueId < numValuesToReplace; ++repValueId)
+          {
+            if (valuesToReplaceRange[repValueId] == arrayRange[inputIdx])
+            {
+              arrayRange[inputIdx] = replacementValuesRange[repValueId];
+              break;
+            }
+          }
+        }
+      });
+  }
+};
+
 //----------------------------------------------------------------------------
 bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::Node& node)
 {
@@ -51,8 +115,7 @@ bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::No
   // process "topologies".
   auto topologies = node["topologies"];
 
-  conduit_index_t nchildren = topologies.number_of_children();
-  for (conduit_index_t i = 0; i < nchildren; ++i)
+  for (conduit_index_t i = 0, nchildren = topologies.number_of_children(); i < nchildren; ++i)
   {
     auto child = topologies.child(i);
     try
@@ -88,9 +151,73 @@ bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::No
     return true;
   }
 
+  // read "state/metadata/vtk_fields"
+  std::map<std::string, FieldMetadata> fieldMetadata;
+  if (node.has_path("state/metadata/vtk_fields"))
+  {
+    auto fieldsMetadata = node["state/metadata/vtk_fields"];
+    for (conduit_index_t i = 0, nchildren = fieldsMetadata.number_of_children(); i < nchildren; ++i)
+    {
+      auto fieldMetadataNode = fieldsMetadata.child(i);
+      const auto& name = fieldMetadataNode.name();
+      try
+      {
+        // read values_to_replace and replacement_values if they exist
+        if (fieldMetadataNode.has_path("values_to_replace") &&
+          fieldMetadataNode.has_path("replacement_values"))
+        {
+          auto valuesToReplace = fieldMetadataNode["values_to_replace"];
+          fieldMetadata[name].ValuesToReplace =
+            vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&valuesToReplace));
+          auto replacementValues = fieldMetadataNode["replacement_values"];
+          fieldMetadata[name].ReplacementValues =
+            vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&replacementValues));
+          if (fieldMetadata[name].ValuesToReplace->GetNumberOfTuples() !=
+            fieldMetadata[name].ReplacementValues->GetNumberOfTuples())
+          {
+            vtkLogF(ERROR,
+              "values_to_replace and replacement_values should have equal size for field '%s'.",
+              name.c_str());
+            return false;
+          }
+          if (fieldMetadata[name].ValuesToReplace->GetNumberOfComponents() != 1 ||
+            fieldMetadata[name].ReplacementValues->GetNumberOfComponents() != 1)
+          {
+            vtkLogF(ERROR,
+              "values_to_replace and replacement_values should have 1 component for field '%s'.",
+              name.c_str());
+            return false;
+          }
+        }
+        // read attribute type if it exists
+        if (fieldMetadataNode.has_path("attribute_type"))
+        {
+          const std::string& attributeType = fieldMetadataNode["attribute_type"].as_string();
+          // check if the attribute type is valid
+          if (FieldMetadata::GetDataSetAttributeType(attributeType) !=
+              vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES ||
+            FieldMetadata::IsGhostsAttributeType(attributeType))
+          {
+            fieldMetadata[name].AttributeType = attributeType;
+          }
+          else
+          {
+            vtkLogF(
+              ERROR, "invalid attribute type '%s' for '%s'.", attributeType.c_str(), name.c_str());
+            return false;
+          }
+        }
+      }
+      catch (std::exception& e)
+      {
+        vtkLogF(ERROR, "failed to process '../state/metadata/vtk_fields/%s'.", name.c_str());
+        vtkLogF(ERROR, "ERROR: \n%s\n", e.what());
+        return false;
+      }
+    }
+  }
   auto fields = node["fields"];
-  nchildren = fields.number_of_children();
-  for (conduit_index_t i = 0; i < nchildren; ++i)
+  for (conduit_index_t i = 0, nchildren = fields.number_of_children(); i < nchildren; ++i)
   {
     auto fieldNode = fields.child(i);
     const auto& fieldname = fieldNode.name();
@@ -111,25 +238,68 @@ bool FillPartitionedDataSet(vtkPartitionedDataSet* output, const conduit_cpp::No
       }
       if (dataset_size > 0)
       {
-        vtkSmartPointer<vtkDataArray> array;
+        // This code path should be removed once MCGhostArrayToVTKGhostArray is removed.
         if (fieldname == "ascent_ghosts")
         {
           // convert ascent ghost information into VTK ghost information
           // the VTK array is named vtkDataSetAttributes::GhostArrayName()
           // and has different values.
-          array = vtkConduitArrayUtilities::MCGhostArrayToVTKGhostArray(
+          auto array = vtkConduitArrayUtilities::MCGhostArrayToVTKGhostArray(
             conduit_cpp::c_node(&values), dsa->IsA("vtkCellData"));
+          dsa->AddArray(array);
+          continue;
         }
-        else
+        vtkSmartPointer<vtkDataArray> array =
+          vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&values), fieldname);
+        if (array->GetNumberOfTuples() != dataset->GetNumberOfElements(vtk_association))
+        {
+          throw std::runtime_error("mismatched tuple count!");
+        }
+        if (fieldMetadata.find(fieldname) != fieldMetadata.end())
         {
-          array =
-            vtkConduitArrayUtilities::MCArrayToVTKArray(conduit_cpp::c_node(&values), fieldname);
-          if (array->GetNumberOfTuples() != dataset->GetNumberOfElements(vtk_association))
+          const auto& metadata = fieldMetadata[fieldname];
+          // replace values if needed
+          if (metadata.ValuesToReplace && metadata.ReplacementValues)
+          {
+            ReplaceValuesWorker replaceValuesWorker;
+            if (!vtkArrayDispatch::Dispatch3SameValueType::Execute(metadata.ValuesToReplace.Get(),
+                  metadata.ReplacementValues.Get(), array.Get(), replaceValuesWorker))
+            {
+              replaceValuesWorker(
+                metadata.ValuesToReplace.Get(), metadata.ReplacementValues.Get(), array.Get());
+            }
+          }
+          // extract the attribute type, and change the array name if needed
+          auto dsaAttributeType = vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES;
+          if (!metadata.AttributeType.empty())
           {
-            throw std::runtime_error("mismatched tuple count!");
+            dsaAttributeType = FieldMetadata::GetDataSetAttributeType(metadata.AttributeType);
+            if (FieldMetadata::IsGhostsAttributeType(metadata.AttributeType))
+            {
+              // convert its name to the VTK ghost array name
+              array->SetName(vtkDataSetAttributes::GhostArrayName());
+              // ensure the array is unsigned char
+              if (!array->IsA("vtkUnsignedCharArray"))
+              {
+                auto ghostArray = vtkSmartPointer<vtkUnsignedCharArray>::New();
+                ghostArray->DeepCopy(array);
+                array = ghostArray;
+              }
+            }
           }
+          if (dsaAttributeType != vtkDataSetAttributes::AttributeTypes::NUM_ATTRIBUTES)
+          {
+            dsa->SetAttribute(array, dsaAttributeType);
+          }
+          else
+          {
+            dsa->AddArray(array);
+          }
+        }
+        else
+        {
+          dsa->AddArray(array);
         }
-        dsa->AddArray(array);
       }
     }
     catch (std::exception& e)