Repository: materialsvirtuallab/monty

Issue:
[Bug]: `Path` objects cause issues with JSON serialization
### Email (Optional)

_No response_

### Version

2024.2.6

### Which OS(es) are you using?

- [ ] MacOS
- [ ] Windows
- [ ] Linux

### What happened?

It would be nice to natively support the `pathlib.Path` object seeing as it is a first-party import in Python.

### Code snippet

```python
from monty.serialization import dumpfn, loadfn
from pathlib import Path

d = {"directory": Path.cwd()}
dumpfn(d, "test.json")
```


### Log output

```shell
TypeError: Object of type WindowsPath is not JSON serializable
```

(or similar if not on Linux)
```


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

Discussion:

Patch:
diff --git a/monty/json.py b/monty/json.py
index 0684956..e82ebf6 100644
--- a/monty/json.py
+++ b/monty/json.py
@@ -13,6 +13,7 @@ from enum import Enum
 from hashlib import sha1
 from importlib import import_module
 from inspect import getfullargspec
+from pathlib import Path
 from uuid import UUID
 
 try:
@@ -380,6 +381,8 @@ class MontyEncoder(json.JSONEncoder):
             return {"@module": "datetime", "@class": "datetime", "string": str(o)}
         if isinstance(o, UUID):
             return {"@module": "uuid", "@class": "UUID", "string": str(o)}
+        if isinstance(o, Path):
+            return {"@module": "pathlib", "@class": "Path", "string": str(o)}
 
         if torch is not None and isinstance(o, torch.Tensor):
             # Support for Pytorch Tensors.
@@ -543,6 +546,9 @@ class MontyDecoder(json.JSONDecoder):
                     if modname == "uuid" and classname == "UUID":
                         return UUID(d["string"])
 
+                    if modname == "pathlib" and classname == "Path":
+                        return Path(d["string"])
+
                     mod = __import__(modname, globals(), locals(), [classname], 0)
                     if hasattr(mod, classname):
                         cls_ = getattr(mod, classname)
diff --git a/tasks.py b/tasks.py
index 5e1a536..f0948a4 100755
--- a/tasks.py
+++ b/tasks.py
@@ -1,5 +1,6 @@
 """Deployment file to facilitate releases of monty."""
 
+
 import datetime
 import glob
 import json


Test patch:
diff --git a/tests/test_json.py b/tests/test_json.py
index 910f461..44c83c4 100644
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -433,6 +433,15 @@ class TestJson:
         d = json.loads(jsonstr, cls=MontyDecoder)
         assert isinstance(d["uuid"], UUID)
 
+    def test_path(self):
+        from pathlib import Path
+
+        p = Path("/home/user/")
+        jsonstr = json.dumps(p, cls=MontyEncoder)
+        d = json.loads(jsonstr, cls=MontyDecoder)
+        assert isinstance(d, Path)
+        assert d == p
+
     def test_nan(self):
         x = [float("NaN")]
         djson = json.dumps(x, cls=MontyEncoder)