- $ref: 'components.yml#/components/parameters/anchorNameInPath'
       - $ref: 'components.yml#/components/parameters/requiredXpathInQuery'
       - $ref: 'components.yml#/components/parameters/observedTimestampInQuery'
+      - $ref: 'components.yml#/components/parameters/contentTypeInHeader'
     requestBody:
       required: true
       content:
         application/json:
           schema:
-            type: object
+            type: string
           examples:
             dataSample:
               $ref: 'components.yml#/components/examples/dataSample'
+        application/xml:
+          schema:
+            type: object
+            xml:
+              name: stores
+          examples:
+            dataSample:
+              $ref: 'components.yml#/components/examples/dataSampleXml'
     responses:
       '200':
         $ref: 'components.yml#/components/responses/Ok'
 
     @Override
     public ResponseEntity<Object> replaceListContent(final String apiVersion, final String dataspaceName,
                                                      final String anchorName, final String parentNodeXpath,
-                                                     final Object jsonData, final String observedTimestamp) {
+                                                     final String nodeData, final String observedTimestamp,
+                                                     final String contentTypeInHeader) {
+        final ContentType contentType = ContentType.fromString(contentTypeInHeader);
         cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath,
-                jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp));
+                nodeData, toOffsetDateTime(observedTimestamp), contentType);
         return new ResponseEntity<>(HttpStatus.OK);
     }
 
         return new ResponseEntity<>(jsonObjectMapper.asJsonString(deltaBetweenAnchors), HttpStatus.OK);
     }
 
-    ResponseEntity<Object> buildResponseEntity(final List<Map<String, Object>> dataMaps,
+    private ResponseEntity<Object> buildResponseEntity(final List<Map<String, Object>> dataMaps,
                                                final ContentType contentType) {
         final String responseData;
-        if (contentType == ContentType.XML) {
+        if (ContentType.XML.equals(contentType)) {
             responseData = XmlFileUtils.convertDataMapsToXml(dataMaps);
         } else {
             responseData = jsonObjectMapper.asJsonString(dataMaps);
 
     private ResponseEntity<Object> buildResponseEntity(final List<Map<String, Object>> dataNodesAsListOfMaps,
                                                final ContentType contentType) {
         final String responseData;
-        if (contentType == ContentType.XML) {
+        if (ContentType.XML.equals(contentType)) {
             responseData = XmlFileUtils.convertDataMapsToXml(dataNodesAsListOfMaps);
         } else {
             responseData = jsonObjectMapper.asJsonString(dataNodesAsListOfMaps);
 
             response.status == expectedHttpStatus.value()
         and: 'the java API was called with the correct parameters'
             expectedApiCount * mockCpsDataService.replaceListContent(dataspaceName, anchorName, 'parent xpath', expectedJsonData,
-                { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) })
+                { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, ContentType.JSON)
+        where:
+            scenario                          | observedTimestamp              || expectedApiCount | expectedHttpStatus
+            'with observed-timestamp'         | '2021-03-03T23:59:59.999-0400' || 1                | HttpStatus.OK
+            'without observed-timestamp'      | null                           || 1                | HttpStatus.OK
+            'with invalid observed-timestamp' | 'invalid'                      || 0                | HttpStatus.BAD_REQUEST
+    }
+
+    def 'Replace list XML content #scenario.'() {
+        when: 'list-nodes endpoint is invoked with put (update) operation'
+            def putRequestBuilder = put("$dataNodeBaseEndpointV1/anchors/$anchorName/list-nodes")
+                .contentType(MediaType.APPLICATION_XML)
+                .param('xpath', 'parent xpath')
+                .content(requestBodyXml)
+            if (observedTimestamp != null)
+                putRequestBuilder.param('observed-timestamp', observedTimestamp)
+            def response = mvc.perform(putRequestBuilder).andReturn().response
+        then: 'a success response is returned'
+            response.status == expectedHttpStatus.value()
+        and: 'the java API was called with the correct parameters'
+            expectedApiCount * mockCpsDataService.replaceListContent(dataspaceName, anchorName, 'parent xpath', expectedXmlData,
+                { it == DateTimeUtility.toOffsetDateTime(observedTimestamp) }, ContentType.XML)
         where:
             scenario                          | observedTimestamp              || expectedApiCount | expectedHttpStatus
             'with observed-timestamp'         | '2021-03-03T23:59:59.999-0400' || 1                | HttpStatus.OK
 
      * @param dataspaceName     dataspace name
      * @param anchorName        anchor name
      * @param parentNodeXpath   parent node xpath
-     * @param jsonData          json data representing the new list elements
+     * @param nodeData          node data representing the new list elements
      * @param observedTimestamp observedTimestamp
+     * @param contentType       JSON/XML content type
      */
-    void replaceListContent(String dataspaceName, String anchorName, String parentNodeXpath, String jsonData,
-        OffsetDateTime observedTimestamp);
+    void replaceListContent(String dataspaceName, String anchorName, String parentNodeXpath, String nodeData,
+        OffsetDateTime observedTimestamp, ContentType contentType);
 
     /**
      * Replaces list content by removing all existing elements and inserting the given new elements as data nodes
 
     @Timed(value = "cps.data.service.list.update",
         description = "Time taken to update a list")
     public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
-            final String jsonData, final OffsetDateTime observedTimestamp) {
+            final String nodeData, final OffsetDateTime observedTimestamp, final ContentType contentType) {
         cpsValidator.validateNameCharacters(dataspaceName, anchorName);
         final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName);
         final Collection<DataNode> newListElements =
-            buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, jsonData, ContentType.JSON);
+            buildDataNodesWithParentNodeXpath(anchor, parentNodeXpath, nodeData, contentType);
         replaceListContent(dataspaceName, anchorName, parentNodeXpath, newListElements, observedTimestamp);
     }
 
 
             assert thrownUp == originalException
     }
 
-    def 'Replace list content data fragment under parent node.'() {
+    def 'Replace list content data fragment JSON under parent node.'() {
         given: 'schema set for given anchor and dataspace references test-tree model'
             setupSchemaSetMocks('test-tree.yang')
         when: 'replace list data method is invoked with list element json data'
             def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
-            objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
+            objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp, ContentType.JSON)
         then: 'the persistence service method is invoked with correct parameters'
             1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree',
                 { dataNodeCollection ->
             2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
     }
 
+    def 'Replace list content data fragment XML under parent node.'() {
+        given: 'schema set for given anchor and dataspace references test-tree model'
+            setupSchemaSetMocks('test-tree.yang')
+        when: 'replace list data method is invoked with list element xml data'
+            def nodeData = '<branch><name>A</name></branch>'
+            objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', nodeData, observedTimestamp, ContentType.XML)
+        then: 'the persistence service method is invoked with correct parameters'
+            1 * mockCpsDataPersistenceService.replaceListContent(dataspaceName, anchorName, '/test-tree',
+                { dataNodeCollection ->
+                    {
+                        assert dataNodeCollection.size() == 1
+                        assert dataNodeCollection.collect { it.getXpath() }
+                            .containsAll(['/test-tree/branch[@name=\'A\']'])
+                    }
+                }
+            )
+        and: 'the CpsValidator is called on the dataspaceName and AnchorName twice'
+            2 * mockCpsValidator.validateNameCharacters(dataspaceName, anchorName)
+    }
+
     def 'Replace whole list content with empty list element.'() {
         given: 'schema set for given anchor and dataspace references test-tree model'
             setupSchemaSetMocks('test-tree.yang')
         when: 'replace list data method is invoked with empty list'
             def jsonData = '{"branch": []}'
-            objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp)
+            objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', jsonData, observedTimestamp, ContentType.JSON)
+        then: 'invalid data exception is thrown'
+            thrown(DataValidationException)
+    }
+
+    def 'Replace whole list content XML with empty list element.'() {
+        given: 'schema set for given anchor and dataspace references test-tree model'
+            setupSchemaSetMocks('test-tree.yang')
+        when: 'replace list data method is invoked with xml empty list'
+            def nodeData = '[]'
+            objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', nodeData, observedTimestamp, ContentType.XML)
         then: 'invalid data exception is thrown'
             thrown(DataValidationException)
     }
 
         schema:
           example: 2021-03-21T00:10:34.030-0100
           type: string
+      - description: Content type in header
+        in: header
+        name: Content-Type
+        required: false
+        schema:
+          default: application/json
+          enum:
+          - application/json
+          - application/xml
+          type: string
       requestBody:
         content:
           application/json:
               dataSample:
                 $ref: '#/components/examples/dataSample'
                 value: null
+            schema:
+              type: string
+          application/xml:
+            examples:
+              dataSample:
+                $ref: '#/components/examples/dataSampleXml'
+                value: null
             schema:
               type: object
+              xml:
+                name: stores
         required: true
       responses:
         "200":
 
 
     def 'Attempt to add empty lists.'() {
         when: 'the batches of new list element(s) are saved'
-            objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', [ ], now)
-        then: 'an admin exception is thrown'
-            thrown(CpsAdminException)
+            objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1 , '/bookstore', [ ] as String, now, ContentType.JSON)
+        then: 'an data exception is thrown'
+            thrown(DataValidationException)
     }
 
     def 'Add child error scenario: #scenario.'() {
             assert countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="2"]', DIRECT_CHILDREN_ONLY)) > 1
         when: 'the categories list is replaced with just category "1" and without child nodes (books)'
             def json = '{"categories": [ {"code":"' +categoryCode + '"' + childJson + '} ] }'
-            objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now)
+            objectUnderTest.replaceListContent(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore', json, now, ContentType.JSON)
         then: 'the new replaced category can be retrieved but has no children anymore'
             assert expectedNumberOfDataNodes == countDataNodesInTree(objectUnderTest.getDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, '/bookstore/categories[@code="' +categoryCode + '"]', DIRECT_CHILDREN_ONLY))
         when: 'attempt to retrieve a category (code) not in the new list'
 
             def jsonListData = generateJsonForOpenRoadmDevices(startId, totalNodes, changeLeaves)
         when: 'the container node is updated'
             resourceMeter.start()
-            objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now)
+            objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now, ContentType.JSON)
             resourceMeter.stop()
         then: 'there are the expected number of total nodes'
             assert totalNodes == countDataNodes('/openroadm-devices/openroadm-device')
     def 'Update leaves for 100 data nodes.'() {
         given: 'there are 200 existing data nodes'
             def jsonListData = generateJsonForOpenRoadmDevices(1, 200, false)
-            objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now)
+            objectUnderTest.replaceListContent(CPS_PERFORMANCE_TEST_DATASPACE, UPDATE_TEST_ANCHOR, '/openroadm-devices', jsonListData, now, ContentType.JSON)
         and: 'JSON for updated data leaves of 100 nodes'
             def jsonDataUpdated  = "{'openroadm-device':[" + (1..100).collect {"{'device-id':'C201-7-1A-" + it + "','status':'fail','ne-state':'jeopardy'}" }.join(",") + "]}"
         when: 'update is performed for leaves'