diff --git a/vendor/magento/module-versions-cms/Block/Adminhtml/Cms/Page/Edit/Tab/Hierarchy.php b/vendor/magento/module-versions-cms/Block/Adminhtml/Cms/Page/Edit/Tab/Hierarchy.php
index 6426aed16d5..57d563d1639 100644
--- a/vendor/magento/module-versions-cms/Block/Adminhtml/Cms/Page/Edit/Tab/Hierarchy.php
+++ b/vendor/magento/module-versions-cms/Block/Adminhtml/Cms/Page/Edit/Tab/Hierarchy.php
@@ -5,13 +5,26 @@
  */
 namespace Magento\VersionsCms\Block\Adminhtml\Cms\Page\Edit\Tab;
 
+use InvalidArgumentException;
+use Magento\Backend\Block\Template;
+use Magento\Backend\Block\Template\Context;
+use Magento\Backend\Block\Widget\Tab\TabInterface;
+use Magento\Cms\Model\Page;
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Json\DecoderInterface;
+use Magento\Framework\Json\EncoderInterface;
+use Magento\Framework\Phrase;
+use Magento\Framework\Registry;
 use Magento\Store\Model\StoreManagerInterface;
+use Magento\VersionsCms\Helper\Hierarchy as HierarchyHelper;
 use Magento\VersionsCms\Model\Hierarchy\Node;
+use Magento\VersionsCms\Model\ResourceModel\Hierarchy\Node\Collection;
+use Magento\VersionsCms\Model\ResourceModel\Hierarchy\Node\CollectionFactory;
 
 /**
  * Cms Page Edit Hierarchy Tab Block
  */
-class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Backend\Block\Widget\Tab\TabInterface
+class Hierarchy extends Template implements TabInterface
 {
     /**
      * Array of nodes for tree
@@ -21,31 +34,27 @@ class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Back
     protected $_nodes = null;
 
     /**
-     * Cms hierarchy
-     *
-     * @var \Magento\VersionsCms\Helper\Hierarchy
+     * @var HierarchyHelper
      */
     protected $_cmsHierarchy;
 
     /**
-     * Core registry
-     *
-     * @var \Magento\Framework\Registry
+     * @var Registry
      */
     protected $_coreRegistry;
 
     /**
-     * @var \Magento\VersionsCms\Model\ResourceModel\Hierarchy\Node\CollectionFactory
+     * @var CollectionFactory
      */
     protected $_nodeCollectionFactory;
 
     /**
-     * @var \Magento\Framework\Json\EncoderInterface
+     * @var EncoderInterface
      */
     protected $_jsonEncoder;
 
     /**
-     * @var \Magento\Framework\Json\DecoderInterface
+     * @var DecoderInterface
      */
     protected $_jsonDecoder;
 
@@ -60,22 +69,22 @@ class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Back
     private $storeManager;
 
     /**
-     * @param \Magento\Backend\Block\Template\Context $context
-     * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder
-     * @param \Magento\Framework\Json\DecoderInterface $jsonDecoder
-     * @param \Magento\VersionsCms\Helper\Hierarchy $cmsHierarchy
-     * @param \Magento\Framework\Registry $registry
-     * @param \Magento\VersionsCms\Model\ResourceModel\Hierarchy\Node\CollectionFactory $nodeCollectionFactory
+     * @param Context $context
+     * @param EncoderInterface $jsonEncoder
+     * @param DecoderInterface $jsonDecoder
+     * @param HierarchyHelper $cmsHierarchy
+     * @param Registry $registry
+     * @param CollectionFactory $nodeCollectionFactory
      * @param StoreManagerInterface $storeManager
      * @param array $data
      */
     public function __construct(
-        \Magento\Backend\Block\Template\Context $context,
-        \Magento\Framework\Json\EncoderInterface $jsonEncoder,
-        \Magento\Framework\Json\DecoderInterface $jsonDecoder,
-        \Magento\VersionsCms\Helper\Hierarchy $cmsHierarchy,
-        \Magento\Framework\Registry $registry,
-        \Magento\VersionsCms\Model\ResourceModel\Hierarchy\Node\CollectionFactory $nodeCollectionFactory,
+        Context $context,
+        EncoderInterface $jsonEncoder,
+        DecoderInterface $jsonDecoder,
+        HierarchyHelper $cmsHierarchy,
+        Registry $registry,
+        CollectionFactory $nodeCollectionFactory,
         StoreManagerInterface $storeManager,
         array $data = []
     ) {
@@ -91,7 +100,7 @@ class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Back
     /**
      * Retrieve current page instance
      *
-     * @return \Magento\Cms\Model\Page
+     * @return Page
      */
     public function getPage()
     {
@@ -125,11 +134,11 @@ class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Back
                     $data = $this->_jsonDecoder->decode($jsonData);
                 }
                 // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock
-            } catch (\Zend_Json_Exception $e) {
+            } catch (InvalidArgumentException $e) {
                 // continue and use the collection to get the node data
             }
 
-            /** @var \Magento\VersionsCms\Model\ResourceModel\Hierarchy\Node\Collection $collection */
+            /** @var Collection $collection */
             $collection = $this->_nodeCollectionFactory
                 ->create()
                 ->joinCmsPage()
@@ -191,7 +200,7 @@ class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Back
      * @param int $scopeId
      * @param string $scopeCode
      * @return string
-     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @throws NoSuchEntityException
      */
     private function getNodeStoreName(int $scopeId, string $scopeCode = Node::NODE_SCOPE_STORE)
     {
@@ -275,7 +284,7 @@ class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Back
     /**
      * Retrieve Tab label
      *
-     * @return \Magento\Framework\Phrase
+     * @return Phrase
      */
     public function getTabLabel()
     {
@@ -285,7 +294,7 @@ class Hierarchy extends \Magento\Backend\Block\Template implements \Magento\Back
     /**
      * Retrieve Tab title
      *
-     * @return \Magento\Framework\Phrase
+     * @return Phrase
      */
     public function getTabTitle()
     {
diff --git a/vendor/magento/module-versions-cms/Controller/Adminhtml/Cms/Hierarchy/Save.php b/vendor/magento/module-versions-cms/Controller/Adminhtml/Cms/Hierarchy/Save.php
index a91b2351ceb..079fd886de1 100644
--- a/vendor/magento/module-versions-cms/Controller/Adminhtml/Cms/Hierarchy/Save.php
+++ b/vendor/magento/module-versions-cms/Controller/Adminhtml/Cms/Hierarchy/Save.php
@@ -6,7 +6,17 @@
  */
 namespace Magento\VersionsCms\Controller\Adminhtml\Cms\Hierarchy;
 
-class Save extends \Magento\VersionsCms\Controller\Adminhtml\Cms\Hierarchy
+use Exception;
+use InvalidArgumentException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Json\Helper\Data;
+use Magento\VersionsCms\Controller\Adminhtml\Cms\Hierarchy;
+use Magento\VersionsCms\Model\Hierarchy\Node;
+
+/**
+ * @SuppressWarnings(PHPMD.AllPurposeAction)
+ */
+class Save extends Hierarchy
 {
     /**
      * Save changes
@@ -19,9 +29,9 @@ class Save extends \Magento\VersionsCms\Controller\Adminhtml\Cms\Hierarchy
     {
         $this->_initScope();
         if ($this->getRequest()->isPost()) {
-            /** @var $node \Magento\VersionsCms\Model\Hierarchy\Node */
+            /** @var $node Node */
             $node = $this->_objectManager->create(
-                \Magento\VersionsCms\Model\Hierarchy\Node::class,
+                Node::class,
                 ['data' => ['scope' => $this->_scope, 'scope_id' => $this->_scopeId]]
             );
             $data = $this->getRequest()->getPostValue();
@@ -34,11 +44,11 @@ class Save extends \Magento\VersionsCms\Controller\Adminhtml\Cms\Hierarchy
                     if (!empty($data['nodes_data'])) {
                         try {
                             $nodesData = $this->_objectManager->get(
-                                \Magento\Framework\Json\Helper\Data::class
+                                Data::class
                             )->jsonDecode(
                                 $data['nodes_data']
                             );
-                        } catch (\Zend_Json_Exception $e) {
+                        } catch (InvalidArgumentException $e) {
                             $nodesData = [];
                         }
                     } else {
@@ -72,9 +82,9 @@ class Save extends \Magento\VersionsCms\Controller\Adminhtml\Cms\Hierarchy
                 if (!empty($nodesData)) {
                     $this->messageManager->addSuccess(__('You have saved the hierarchy.'));
                 }
-            } catch (\Magento\Framework\Exception\LocalizedException $e) {
+            } catch (LocalizedException $e) {
                 $this->messageManager->addError($e->getMessage());
-            } catch (\Exception $e) {
+            } catch (Exception $e) {
                 $this->messageManager->addException($e, __('Something went wrong while saving the hierarchy.'));
             }
 
diff --git a/vendor/magento/module-versions-cms/Observer/Backend/CmsPageSaveBeforeObserver.php b/vendor/magento/module-versions-cms/Observer/Backend/CmsPageSaveBeforeObserver.php
index cd94663f809..59440627656 100644
--- a/vendor/magento/module-versions-cms/Observer/Backend/CmsPageSaveBeforeObserver.php
+++ b/vendor/magento/module-versions-cms/Observer/Backend/CmsPageSaveBeforeObserver.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\VersionsCms\Observer\Backend;
 
+use InvalidArgumentException;
 use Magento\Cms\Model\Page;
 use Magento\Framework\Event\Observer as EventObserver;
 use Magento\Framework\Event\ObserverInterface;
@@ -63,7 +64,7 @@ class CmsPageSaveBeforeObserver implements ObserverInterface
         if ($nodesData !== null) {
             try {
                 $nodesData = $this->jsonHelper->jsonDecode($nodesData);
-            } catch (\Zend_Json_Exception $e) {
+            } catch (InvalidArgumentException $e) {
                 $nodesData = null;
             }
             if (!empty($nodesData)) {
diff --git a/vendor/magento/module-visual-merchandiser/Block/Adminhtml/Category/Merchandiser.php b/vendor/magento/module-visual-merchandiser/Block/Adminhtml/Category/Merchandiser.php
index f652ba28a6d..5203bf246c8 100644
--- a/vendor/magento/module-visual-merchandiser/Block/Adminhtml/Category/Merchandiser.php
+++ b/vendor/magento/module-visual-merchandiser/Block/Adminhtml/Category/Merchandiser.php
@@ -5,34 +5,39 @@
  */
 namespace Magento\VisualMerchandiser\Block\Adminhtml\Category;
 
+use Magento\Backend\Block\Template;
+use Magento\Backend\Block\Widget\Context;
+use Magento\Framework\Registry;
+use Magento\VisualMerchandiser\Model\Position\Cache;
+
 /**
  * @api
  * @since 100.1.0
  */
-class Merchandiser extends \Magento\Backend\Block\Template
+class Merchandiser extends Template
 {
     /**
-     * @var \Magento\Framework\Registry
+     * @var Registry
      * @since 100.1.0
      */
     protected $_coreRegistry;
 
     /**
-     * @var \Magento\VisualMerchandiser\Model\Position\Cache
+     * @var Cache
      * @since 100.1.0
      */
     protected $_positionCache;
 
     /**
-     * @param \Magento\Backend\Block\Widget\Context $context
-     * @param \Magento\Framework\Registry $registry
-     * @param \Magento\VisualMerchandiser\Model\Position\Cache $cache
+     * @param Context $context
+     * @param Registry $registry
+     * @param Cache $cache
      * @param array $data
      */
     public function __construct(
-        \Magento\Backend\Block\Widget\Context $context,
-        \Magento\Framework\Registry $registry,
-        \Magento\VisualMerchandiser\Model\Position\Cache $cache,
+        Context $context,
+        Registry $registry,
+        Cache $cache,
         array $data = []
     ) {
         $this->_coreRegistry = $registry;
@@ -41,6 +46,8 @@ class Merchandiser extends \Magento\Backend\Block\Template
     }
 
     /**
+     * Get dialog URL
+     *
      * @return string
      * @since 100.1.0
      */
@@ -50,12 +57,15 @@ class Merchandiser extends \Magento\Backend\Block\Template
             'merchandiser/*/addproduct',
             [
                 'cache_key' => $this->getPositionCacheKey(),
-                'componentJson' => true
+                'componentJson' => true,
+                'category_id' => $this->getCategoryId()
             ]
         );
     }
 
     /**
+     * Get save positions URL
+     *
      * @return string
      * @since 100.1.0
      */
@@ -65,7 +75,7 @@ class Merchandiser extends \Magento\Backend\Block\Template
     }
 
     /**
-     * Get products positions url
+     * Get products positions URL
      *
      * @return string
      * @since 100.1.0
@@ -76,6 +86,8 @@ class Merchandiser extends \Magento\Backend\Block\Template
     }
 
     /**
+     * Get category id
+     *
      * @return mixed
      * @since 100.1.0
      */
@@ -85,6 +97,8 @@ class Merchandiser extends \Magento\Backend\Block\Template
     }
 
     /**
+     * Get position cache key
+     *
      * @return string
      * @since 100.1.0
      */
@@ -94,20 +108,24 @@ class Merchandiser extends \Magento\Backend\Block\Template
     }
 
     /**
+     * Get position cache key name
+     *
      * @return string
      * @since 100.1.0
      */
     public function getPositionCacheKeyName()
     {
-        return \Magento\VisualMerchandiser\Model\Position\Cache::POSITION_CACHE_KEY;
+        return Cache::POSITION_CACHE_KEY;
     }
 
     /**
+     * Get position data JSON
+     *
      * @return string
      * @since 100.1.0
      */
     public function getPositionDataJson()
     {
-        return \Zend_Json::encode($this->_positionCache->getPositions($this->getPositionCacheKey()));
+        return json_encode($this->_positionCache->getPositions($this->getPositionCacheKey()));
     }
 }
diff --git a/vendor/magento/module-visual-merchandiser/Controller/Adminhtml/Position/Get.php b/vendor/magento/module-visual-merchandiser/Controller/Adminhtml/Position/Get.php
index 4adb36062fb..8b5e67654dd 100644
--- a/vendor/magento/module-visual-merchandiser/Controller/Adminhtml/Position/Get.php
+++ b/vendor/magento/module-visual-merchandiser/Controller/Adminhtml/Position/Get.php
@@ -6,30 +6,22 @@
 namespace Magento\VisualMerchandiser\Controller\Adminhtml\Position;
 
 use Magento\Framework\App\Action\HttpPostActionInterface;
+use Magento\Framework\Controller\ResultInterface;
+use Magento\VisualMerchandiser\Controller\Adminhtml\Position;
+use Magento\VisualMerchandiser\Model\Position\Cache;
 
-/**
- * Class Get
- *
- * @package Magento\VisualMerchandiser\Controller\Adminhtml\Position
- */
-class Get extends \Magento\VisualMerchandiser\Controller\Adminhtml\Position implements HttpPostActionInterface
+class Get extends Position implements HttpPostActionInterface
 {
     /**
      * Get products positions from cache
      *
-     * @return \Magento\Framework\Controller\ResultInterface
+     * @return ResultInterface
      */
     public function execute()
     {
-        /** @var \Magento\Framework\Controller\Result\Json $resultJson */
         $resultJson = $this->resultJsonFactory->create();
-
-        $cacheKey = $this->getRequest()->getParam(
-            \Magento\VisualMerchandiser\Model\Position\Cache::POSITION_CACHE_KEY
-        );
-
-        $positions = \Zend_Json::encode($this->cache->getPositions($cacheKey));
-
+        $cacheKey = $this->getRequest()->getParam(Cache::POSITION_CACHE_KEY);
+        $positions = json_encode($this->cache->getPositions($cacheKey));
         $resultJson->setData($positions);
 
         return $resultJson;
diff --git a/vendor/magento/module-visual-merchandiser/Model/Category/Products.php b/vendor/magento/module-visual-merchandiser/Model/Category/Products.php
index b64f5eb479c..8a809eaae39 100644
--- a/vendor/magento/module-visual-merchandiser/Model/Category/Products.php
+++ b/vendor/magento/module-visual-merchandiser/Model/Category/Products.php
@@ -7,6 +7,7 @@ declare(strict_types=1);
 
 namespace Magento\VisualMerchandiser\Model\Category;
 
+use InvalidArgumentException;
 use Magento\Catalog\Model\Category\Product\PositionResolver;
 use Magento\Catalog\Model\ProductFactory;
 use Magento\Catalog\Model\ResourceModel\Product\Collection;
@@ -16,8 +17,8 @@ use Magento\Framework\DB\Select;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Framework\Module\Manager;
 use Magento\VisualMerchandiser\Model\Category\Position\TemporaryTableFactory;
-use Magento\VisualMerchandiser\Model\Resolver\QuantityAndStock;
 use Magento\VisualMerchandiser\Model\Position\Cache;
+use Magento\VisualMerchandiser\Model\Resolver\QuantityAndStock;
 use Magento\VisualMerchandiser\Model\Sorting;
 use Zend_Db_Expr;
 use Zend_Db_Select_Exception;
@@ -131,7 +132,7 @@ class Products
      * @param array|null $productPositions (Optional)
      * @return Collection
      * @throws LocalizedException
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function getCollectionForGrid($categoryId, $store = null, $productPositions = null)
     {
@@ -238,7 +239,7 @@ class Products
      *
      * @param Collection $collection
      * @return Collection
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function applyCachedChanges(Collection $collection)
     {
@@ -277,7 +278,7 @@ class Products
      * Retrieves positions
      *
      * @return array|bool
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     private function getPositions()
     {
diff --git a/vendor/magento/module-visual-merchandiser/Model/Position/Cache.php b/vendor/magento/module-visual-merchandiser/Model/Position/Cache.php
index 56bc3689320..ef9ec1b3304 100755
--- a/vendor/magento/module-visual-merchandiser/Model/Position/Cache.php
+++ b/vendor/magento/module-visual-merchandiser/Model/Position/Cache.php
@@ -5,21 +5,31 @@
  */
 namespace Magento\VisualMerchandiser\Model\Position;
 
+use InvalidArgumentException;
+use Magento\Backend\Model\Auth\Session;
+use Magento\Config\Model\Config;
+use Magento\Framework\App\CacheInterface;
+use Magento\Framework\Data\Collection\AbstractDb;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Framework\Model\Context;
+use Magento\Framework\Model\ResourceModel\AbstractResource;
+use Magento\Framework\Registry;
+
 /**
  * Products positions cache model
  */
-class Cache extends \Magento\Framework\Model\AbstractModel
+class Cache extends AbstractModel
 {
-    const POSITION_CACHE_KEY = 'position_cache_key';
-    const CACHE_PREFIX = 'MERCHANDISER_POSITION_CACHE_';
+    public const POSITION_CACHE_KEY = 'position_cache_key';
+    public const CACHE_PREFIX = 'MERCHANDISER_POSITION_CACHE_';
 
     /**
-     * @var \Magento\Framework\App\CacheInterface
+     * @var CacheInterface
      */
     protected $cache;
 
     /**
-     * @var \Magento\Config\Model\Config
+     * @var Config
      */
     protected $backendConfig;
 
@@ -29,19 +39,19 @@ class Cache extends \Magento\Framework\Model\AbstractModel
     protected $cachedData = null;
 
     /**
-     * @param \Magento\Framework\Model\Context $context
-     * @param \Magento\Framework\Registry $registry
-     * @param \Magento\Config\Model\Config $backendConfig
-     * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
-     * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
+     * @param Context $context
+     * @param Registry $registry
+     * @param Config $backendConfig
+     * @param AbstractResource $resource
+     * @param AbstractDb $resourceCollection
      * @param array $data
      */
     public function __construct(
-        \Magento\Framework\Model\Context $context,
-        \Magento\Framework\Registry $registry,
-        \Magento\Config\Model\Config $backendConfig,
-        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
-        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
+        Context $context,
+        Registry $registry,
+        Config $backendConfig,
+        AbstractResource $resource = null,
+        AbstractDb $resourceCollection = null,
         array $data = []
     ) {
         parent::__construct($context, $registry, $resource, $resourceCollection, $data);
@@ -64,7 +74,7 @@ class Cache extends \Magento\Framework\Model\AbstractModel
         }
 
         $lifeTime = $this->backendConfig->getConfigDataValue(
-            \Magento\Backend\Model\Auth\Session::XML_PATH_SESSION_LIFETIME
+            Session::XML_PATH_SESSION_LIFETIME
         );
 
         if (!is_numeric($lifeTime)) {
@@ -78,7 +88,7 @@ class Cache extends \Magento\Framework\Model\AbstractModel
         }
 
         $this->cachedData = null;
-        $saveResult = $this->cache->save(\Zend_Json::encode($data), self::CACHE_PREFIX . $key, [], $lifeTime);
+        $saveResult = $this->cache->save(json_encode($data), self::CACHE_PREFIX . $key, [], $lifeTime);
         if ($saveResult) {
             $this->cachedData = $data;
         }
@@ -90,7 +100,7 @@ class Cache extends \Magento\Framework\Model\AbstractModel
      * @param string $cacheKey
      * @param string $param
      * @return null|mixed
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     private function getFromCache($cacheKey, $param)
     {
@@ -101,7 +111,10 @@ class Cache extends \Magento\Framework\Model\AbstractModel
         if ($this->cachedData == null) {
             $jsonStr = $this->cache->load(self::CACHE_PREFIX . $cacheKey);
             if (strlen($jsonStr)) {
-                $this->cachedData = \Zend_Json::decode($jsonStr);
+                $this->cachedData = json_decode($jsonStr, true);
+                if (json_last_error() !== JSON_ERROR_NONE) {
+                    throw new InvalidArgumentException("Unable to unserialize value. Error: " . json_last_error_msg());
+                }
             }
         }
 
@@ -113,7 +126,7 @@ class Cache extends \Magento\Framework\Model\AbstractModel
      *
      * @param string $key
      * @return bool|array
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function getPositions($key)
     {
@@ -141,7 +154,7 @@ class Cache extends \Magento\Framework\Model\AbstractModel
      *
      * @param string $key
      * @return bool|int
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function getSortOrder($key)
     {
@@ -154,7 +167,7 @@ class Cache extends \Magento\Framework\Model\AbstractModel
      * @param string $key
      * @param array $data
      * @return void
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function prependPositions($key, $data)
     {
diff --git a/vendor/magento/module-visual-merchandiser/Model/Product/DataProvider.php b/vendor/magento/module-visual-merchandiser/Model/Product/DataProvider.php
index 711d5a5409c..e403151caa2 100755
--- a/vendor/magento/module-visual-merchandiser/Model/Product/DataProvider.php
+++ b/vendor/magento/module-visual-merchandiser/Model/Product/DataProvider.php
@@ -5,8 +5,11 @@
  */
 namespace Magento\VisualMerchandiser\Model\Product;
 
+use InvalidArgumentException;
+use Magento\Catalog\Model\Category\Product\PositionResolver;
 use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
 use Magento\Framework\Api\Filter;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\App\RequestInterface;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Store\Model\Store;
@@ -38,6 +41,11 @@ class DataProvider extends AbstractDataProvider
      */
     private $request;
 
+    /**
+     * @var PositionResolver
+     */
+    private $positionResolver;
+
     /**
      * @param string $name
      * @param string $primaryFieldName
@@ -47,6 +55,7 @@ class DataProvider extends AbstractDataProvider
      * @param Cache $cache
      * @param array $meta
      * @param array $data
+     * @param PositionResolver|null $positionResolver
      * @throws LocalizedException
      */
     public function __construct(
@@ -57,12 +66,14 @@ class DataProvider extends AbstractDataProvider
         RequestInterface $request,
         Cache $cache,
         array $meta = [],
-        array $data = []
+        array $data = [],
+        ?PositionResolver $positionResolver = null
     ) {
         parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
 
         $this->request = $request;
         $this->cache = $cache;
+        $this->positionResolver = $positionResolver ?: ObjectManager::getInstance()->get(PositionResolver::class);
 
         $this->collection = $collectionFactory->create()->addAttributeToSelect(
             'sku'
@@ -126,12 +137,14 @@ class DataProvider extends AbstractDataProvider
             if ('*' == $paramValue) {
                 $paramValue = $this->request->getParam($paramName);
                 $this->positionCacheKey = $paramValue;
+            } elseif ('%category_id%' === $paramValue) {
+                $paramValue = $this->request->getParam($paramName);
             }
 
             if ($paramValue) {
                 $this->data['config']['update_url'] = sprintf(
-                    '%s%s/%s',
-                    $this->data['config']['update_url'],
+                    '%s/%s/%s',
+                    rtrim($this->data['config']['update_url'], '/'),
                     $paramName,
                     $paramValue
                 );
@@ -143,18 +156,21 @@ class DataProvider extends AbstractDataProvider
      * Sets the position values
      *
      * @return void
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function addPositionData()
     {
         $positions = $this->cache->getPositions($this->positionCacheKey);
+        $categoryId = $this->request->getParam('category_id');
 
-        if ($positions === false) {
+        if ($positions === false && $categoryId !== null) {
+            $positions = $this->positionResolver->getPositions((int) $categoryId);
+        } elseif ($positions === false && $categoryId === null) {
             return;
         }
 
         foreach ($this->collection as $item) {
-            if (array_key_exists($item->getEntityId(), $positions)) {
+            if (is_array($positions) && array_key_exists($item->getEntityId(), $positions)) {
                 $item->setPosition(
                     $positions[$item->getEntityId()]
                 );
@@ -172,17 +188,23 @@ class DataProvider extends AbstractDataProvider
      * Get data
      *
      * @return array
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function getData()
     {
         $this->collection->setStoreId(Store::DEFAULT_STORE_ID);
         $this->collection->getLimitationFilters()->setUsePriceIndex(false);
         $this->addPositionData();
+        $positions = $this->cache->getPositions($this->positionCacheKey);
+        $categoryId = $this->request->getParam('category_id');
         $arrItems = [];
         $arrItems['totalRecords'] = $this->collection->getSize();
         $arrItems['items'] = [];
-        $arrItems['selectedData'] = $this->cache->getPositions($this->positionCacheKey);
+        if ($positions === false && $categoryId !== null) {
+            $arrItems['selectedData'] = $this->positionResolver->getPositions((int) $categoryId);
+        } else {
+            $arrItems['selectedData'] = $positions;
+        }
         $arrItems['allIds'] = $this->collection->getAllIds();
 
         foreach ($this->collection->getItems() as $item) {
diff --git a/vendor/magento/module-visual-merchandiser/Model/Rules.php b/vendor/magento/module-visual-merchandiser/Model/Rules.php
index e4a1349b7e2..1fd0c704170 100644
--- a/vendor/magento/module-visual-merchandiser/Model/Rules.php
+++ b/vendor/magento/module-visual-merchandiser/Model/Rules.php
@@ -6,7 +6,11 @@
 
 namespace Magento\VisualMerchandiser\Model;
 
+use InvalidArgumentException;
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Model\Product;
 use Magento\Framework\DB\Select;
+use Magento\Framework\Model\AbstractModel;
 use Magento\Framework\Model\Context;
 use Magento\Framework\Registry;
 use Magento\Catalog\Model\ResourceModel\Product\Collection;
@@ -33,7 +37,7 @@ use Magento\Framework\App\ObjectManager;
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  * @since 100.0.2
  */
-class Rules extends \Magento\Framework\Model\AbstractModel
+class Rules extends AbstractModel
 {
     /**
      * Additional attributes available to smart category rules
@@ -163,9 +167,9 @@ class Rules extends \Magento\Framework\Model\AbstractModel
         try {
             $conditionsJson = $this->getData('conditions_serialized');
             if ($conditionsJson) {
-                \Zend_Json::decode($conditionsJson);
+                $this->decode((string)$conditionsJson);
             }
-        } catch (\Zend_Exception $e) {
+        } catch (InvalidArgumentException $e) {
             $this->_messageManager->addException($e, __("Category rules validation failed"));
             $this->setData('conditions_serialized', null);
         }
@@ -195,7 +199,7 @@ class Rules extends \Magento\Framework\Model\AbstractModel
         $result = [];
         foreach ($attributes as $attributeCode) {
             $attribute = $this->attribute->loadByCode(
-                \Magento\Catalog\Model\Product::ENTITY,
+                Product::ENTITY,
                 $attributeCode
             );
             if (!$attribute->getId()) {
@@ -266,7 +270,7 @@ class Rules extends \Magento\Framework\Model\AbstractModel
      * Get conditions
      *
      * @return mixed|null
-     * @throws \Zend_Json_Exception
+     * @throws InvalidArgumentException
      */
     public function getConditions()
     {
@@ -279,7 +283,7 @@ class Rules extends \Magento\Framework\Model\AbstractModel
             return null;
         }
 
-        return \Zend_Json::decode($conditionsSerialized);
+        return $this->decode((string)$conditionsSerialized);
     }
 
     /**
@@ -299,7 +303,7 @@ class Rules extends \Magento\Framework\Model\AbstractModel
 
         try {
             $conditions = $rules->getConditions();
-        } catch (\Zend_Exception $e) {
+        } catch (InvalidArgumentException $e) {
             $this->_messageManager->addException($e, __("Error in reading category rules"));
             return;
         }
@@ -384,7 +388,7 @@ class Rules extends \Magento\Framework\Model\AbstractModel
         if (!$positions || count($ids) != count($positions) || array_diff($ids, array_keys($positions))) {
             $positions = [];
             foreach ($collection as $key => $item) {
-                /* @var $item \Magento\Catalog\Api\Data\ProductInterface */
+                /* @var $item ProductInterface */
                 $positions[$item->getId()] = $key;
             }
         }
@@ -408,4 +412,22 @@ class Rules extends \Magento\Framework\Model\AbstractModel
         }
         return $productCollection;
     }
+
+    /**
+     * Decode a value
+     *
+     * @param string $encodedValue
+     *
+     * @return mixed
+     * @throws InvalidArgumentException
+     */
+    private function decode(string $encodedValue)
+    {
+        $decoded = json_decode($encodedValue, true);
+        if (json_last_error() !== JSON_ERROR_NONE) {
+            throw new InvalidArgumentException("Unable to unserialize value. Error: " . json_last_error_msg());
+        }
+
+        return $decoded;
+    }
 }
diff --git a/vendor/magento/module-visual-merchandiser/view/adminhtml/ui_component/merchandiser_product_listing.xml b/vendor/magento/module-visual-merchandiser/view/adminhtml/ui_component/merchandiser_product_listing.xml
index c3ca2b6fd3c..848bf1f059d 100755
--- a/vendor/magento/module-visual-merchandiser/view/adminhtml/ui_component/merchandiser_product_listing.xml
+++ b/vendor/magento/module-visual-merchandiser/view/adminhtml/ui_component/merchandiser_product_listing.xml
@@ -22,6 +22,7 @@
         <settings>
             <filterUrlParams>
                 <param name="cache_key">*</param>
+                <param name="category_id">%category_id%</param>
             </filterUrlParams>
             <updateUrl path="mui/index/render"/>
         </settings>
