Repository: pydicom/pydicom

Issue:
Unable to assign single element list to PN field
I am getting `AttributeError` while trying to assign a list of single element to a `PN` field.
It's converting `val` to a 2D array [here](https://github.com/pydicom/pydicom/blob/master/pydicom/filewriter.py#L328) when `VM` is 1. 

**Code**
```
>>> from pydicom import dcmread, dcmwrite
>>> ds = dcmread("SOP1.dcm")
>>> a = ["name1"]
>>> b = ["name1", "name2"]
>>> ds.PatientName = a
>>> dcmwrite("out.dcm", ds)     # throws the error below
>>> ds.PatientName = b
>>> dcmwrite("out.dcm", ds)     # works fine
```

**Error**
```
Traceback (most recent call last):
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/tag.py", line 28, in tag_in_exception
    yield
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 662, in write_dataset
    write_data_element(fp, dataset.get_item(tag), dataset_encoding)
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 562, in write_data_element
    fn(buffer, elem, encodings=encodings)  # type: ignore[operator]
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 333, in write_PN
    enc = b'\\'.join([elem.encode(encodings) for elem in val])
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 333, in <listcomp>
    enc = b'\\'.join([elem.encode(encodings) for elem in val])
AttributeError: 'MultiValue' object has no attribute 'encode'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 1153, in dcmwrite
    _write_dataset(fp, dataset, write_like_original)
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 889, in _write_dataset
    write_dataset(fp, get_item(dataset, slice(0x00010000, None)))
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 662, in write_dataset
    write_data_element(fp, dataset.get_item(tag), dataset_encoding)
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/contextlib.py", line 131, in __exit__
    self.gen.throw(type, value, traceback)
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/tag.py", line 32, in tag_in_exception
    raise type(exc)(msg) from exc
AttributeError: With tag (0010, 0010) got exception: 'MultiValue' object has no attribute 'encode'
Traceback (most recent call last):
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/tag.py", line 28, in tag_in_exception
    yield
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 662, in write_dataset
    write_data_element(fp, dataset.get_item(tag), dataset_encoding)
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 562, in write_data_element
    fn(buffer, elem, encodings=encodings)  # type: ignore[operator]
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 333, in write_PN
    enc = b'\\'.join([elem.encode(encodings) for elem in val])
  File "/Users/neonbulb/virtualenv/deid/lib/python3.8/site-packages/pydicom/filewriter.py", line 333, in <listcomp>
    enc = b'\\'.join([elem.encode(encodings) for elem in val])
AttributeError: 'MultiValue' object has no attribute 'encode'
```


Discussion:
Not sure if this is a bug, but at least it is unexpected behavior. Shall be easy to fix.

Patch:
diff --git a/pydicom/dataelem.py b/pydicom/dataelem.py
--- a/pydicom/dataelem.py
+++ b/pydicom/dataelem.py
@@ -512,9 +512,10 @@ def _convert_value(self, val: Any) -> Any:
             val.append
         except AttributeError:  # not a list
             return self._convert(val)
-        else:
-            return MultiValue(self._convert, val,
-                              validation_mode=self.validation_mode)
+        if len(val) == 1:
+            return self._convert(val[0])
+        return MultiValue(self._convert, val,
+                          validation_mode=self.validation_mode)
 
     def _convert(self, val: Any) -> Any:
         """Convert `val` to an appropriate type for the element's VR."""


Test patch:
diff --git a/pydicom/tests/test_json.py b/pydicom/tests/test_json.py
--- a/pydicom/tests/test_json.py
+++ b/pydicom/tests/test_json.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 # Copyright 2008-2019 pydicom authors. See LICENSE file for details.
 import json
+from unittest import mock
 
 import pytest
 
@@ -271,13 +272,14 @@ def test_sort_order(self):
         assert ds_json.index('"00100020"') < ds_json.index('"00100030"')
         assert ds_json.index('"00100030"') < ds_json.index('"00100040"')
 
-    def test_suppress_invalid_tags(self):
+    @mock.patch("pydicom.DataElement.to_json_dict", side_effect=ValueError)
+    def test_suppress_invalid_tags(self, _):
         """Test tags that raise exceptions don't if suppress_invalid_tags True.
         """
         ds = Dataset()
-        ds.add_new(0x00100010, 'PN', ['Jane^Doe'])
+        ds.add_new(0x00100010, 'PN', 'Jane^Doe')
 
-        with pytest.raises(Exception):
+        with pytest.raises(ValueError):
             ds.to_json_dict()
 
         ds_json = ds.to_json_dict(suppress_invalid_tags=True)
diff --git a/pydicom/tests/test_valuerep.py b/pydicom/tests/test_valuerep.py
--- a/pydicom/tests/test_valuerep.py
+++ b/pydicom/tests/test_valuerep.py
@@ -1540,20 +1540,27 @@ def test_set_value(vr, pytype, vm0, vmN, keyword, disable_value_validation):
         assert value == elem.value
 
     # Test VM = 1
+    if vr != 'SQ':
+        ds = Dataset()
+        value = vmN[0]
+        setattr(ds, keyword, value)
+        elem = ds[keyword]
+        assert elem.value == value
+        assert value == elem.value
+
+    # Test VM = 1 as list
     ds = Dataset()
     value = vmN[0]
+    setattr(ds, keyword, [value])
+    elem = ds[keyword]
     if vr == 'SQ':
-        setattr(ds, keyword, [value])
-        elem = ds[keyword]
         assert elem.value[0] == value
         assert value == elem.value[0]
     else:
-        setattr(ds, keyword, value)
-        elem = ds[keyword]
         assert elem.value == value
         assert value == elem.value
 
-    if vr[0] == 'O':
+    if vr[0] == 'O' or vr == 'UN':
         return
 
     # Test VM > 1