-
-
Notifications
You must be signed in to change notification settings - Fork 32
feat: add xPath support #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Reviewer's GuideAdds an optional XPath 3.1 json-to-xml output mode to json2xml, including a type-aware converter, wiring through the Json2xml API, and comprehensive tests for the new format. Sequence diagram for Json2xml to_xml with xpath_format enabledsequenceDiagram
actor Developer
participant Json2xml
participant dicttoxml
participant XPath31Converter
Developer->>Json2xml: __init__(data, wrapper, root, pretty, attr_type, item_wrap, xpath_format=True)
Developer->>Json2xml: to_xml()
Json2xml->>dicttoxml: dicttoxml(obj=data, custom_root=wrapper, attr_type=attr_type, item_wrap=item_wrap, xpath_format=True)
dicttoxml->>XPath31Converter: convert_to_xpath31(obj)
XPath31Converter-->>dicttoxml: xml_content (map/array/string/number/boolean/null)
dicttoxml->>dicttoxml: wrap root with xmlns=http://www.w3.org/2005/xpath-functions
dicttoxml-->>Json2xml: xml_bytes
Json2xml-->>Developer: xml_bytes or pretty printed XML
Class diagram for updated Json2xml API and XPath 3.1 helpersclassDiagram
class Json2xml {
+dict~str, Any~ data
+list~Any~ data
+str wrapper
+bool root
+bool pretty
+bool attr_type
+bool item_wrap
+bool xpath_format
+Json2xml(data, wrapper, root, pretty, attr_type, item_wrap, xpath_format)
+to_xml() Any
}
class dicttoxml {
+dicttoxml(obj, custom_root, ids, attr_type, item_wrap, item_func, cdata, xml_namespaces, list_headers, xpath_format) bytes
}
class XPath31Converter {
+get_xpath31_tag_name(val) str
+convert_to_xpath31(obj, parent_key) str
}
Json2xml ..> dicttoxml : uses
dicttoxml ..> XPath31Converter : uses when xpath_format
Flow diagram for dicttoxml XPath 3.1 mode selectionflowchart TD
A["Start dicttoxml(obj, ..., xpath_format)"] --> B{xpath_format is True?}
B -- Yes --> C["xml_content = convert_to_xpath31(obj)"]
C --> D{xml_content startswith <map?}
D -- Yes --> E["output_root = replace first <map with <map xmlns=XPATH_FUNCTIONS_NS"]
D -- No --> F{xml_content startswith <array?}
F -- Yes --> G["output_root = replace first <array with <array xmlns=XPATH_FUNCTIONS_NS"]
F -- No --> H["output_root = <map xmlns=XPATH_FUNCTIONS_NS> + xml_content + </map>"]
E --> I["xml = XML declaration + output_root"]
G --> I
H --> I
I --> J["return xml encoded as utf-8"]
B -- No --> K["Proceed with existing dicttoxml logic (namespaces, wrappers, attributes, etc.)"]
K --> L["return xml encoded as utf-8"]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #259 +/- ##
==========================================
+ Coverage 99.30% 99.69% +0.39%
==========================================
Files 3 3
Lines 288 330 +42
==========================================
+ Hits 286 329 +43
+ Misses 2 1 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
get_xpath31_tag_nameis defined but never used; either integrate it intoconvert_to_xpath31(e.g., for tag selection) or remove it to avoid dead code.- In
convert_to_xpath31, theSequencecheck will treatbytes/bytearrayas arrays; if that’s not intended for JSON-like data, consider explicitly excluding those types alongsidestr.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- `get_xpath31_tag_name` is defined but never used; either integrate it into `convert_to_xpath31` (e.g., for tag selection) or remove it to avoid dead code.
- In `convert_to_xpath31`, the `Sequence` check will treat `bytes`/`bytearray` as arrays; if that’s not intended for JSON-like data, consider explicitly excluding those types alongside `str`.
## Individual Comments
### Comment 1
<location> `json2xml/dicttoxml.py:236-245` </location>
<code_context>
+ Returns:
+ str: XML string in XPath 3.1 format.
+ """
+ key_attr = f' key="{escape_xml(parent_key)}"' if parent_key is not None else ""
+
+ if obj is None:
+ return f"<null{key_attr}/>"
+
+ if isinstance(obj, bool):
+ return f"<boolean{key_attr}>{str(obj).lower()}</boolean>"
+
+ if isinstance(obj, (int, float, numbers.Number)):
+ return f"<number{key_attr}>{obj}</number>"
+
+ if isinstance(obj, str):
+ return f"<string{key_attr}>{escape_xml(obj)}</string>"
+
+ if isinstance(obj, dict):
+ children = "".join(convert_to_xpath31(v, k) for k, v in obj.items())
+ return f"<map{key_attr}>{children}</map>"
+
</code_context>
<issue_to_address>
**issue:** Non-string dict keys will raise when building the key attribute instead of being handled gracefully.
In `convert_to_xpath31`, `parent_key` is passed directly to `escape_xml`, which assumes a string. When iterating `for k, v in obj.items()`, non-string keys (ints, enums, tuples, etc.) will cause a `TypeError`. To align with the original `dicttoxml` behavior and keep the function resilient, normalize keys before escaping, e.g.:
```python
key_str = str(parent_key) if parent_key is not None else None
key_attr = f' key="{escape_xml(key_str)}"' if key_str is not None else ""
```
This keeps non-string keys supported while still escaping safely.
</issue_to_address>
### Comment 2
<location> `json2xml/json2xml.py:23-26` </location>
<code_context>
xml_namespaces: dict[str, Any] = {},
- list_headers: bool = False
+ list_headers: bool = False,
+ xpath_format: bool = False,
) -> bytes:
"""
</code_context>
<issue_to_address>
**suggestion:** Json2xml wrapper/root options become ineffective when `xpath_format` is enabled, which could confuse users of this class.
With `xpath_format=True`, `Json2xml.to_xml()` still exposes `wrapper` and `root`, but `dicttoxml`’s XPath mode hardcodes the root element/namespace, so those options have no effect. Please either document that `wrapper`/`root` are ignored when `xpath_format` is enabled, or prevent incompatible combinations (e.g., via validation in `Json2xml`’s constructor).
Suggested implementation:
```python
def __init__(
self,
data: dict[str, Any] | list[Any] | None = None,
wrapper: str = "all",
root: bool = True,
pretty: bool = True,
attr_type: bool = True,
item_wrap: bool = True,
xpath_format: bool = False,
```
```python
if xpath_format and (wrapper != "all" or not root):
raise ValueError(
"Invalid argument combination: when 'xpath_format=True', the "
"'wrapper' and 'root' options are ignored by dicttoxml. "
"Use wrapper='all' and root=True with xpath_format=True, or "
"disable xpath_format if you need custom wrapper/root."
)
self.data = data
```
Because I only see part of the file, you may need to adjust the second SEARCH block so that it matches the actual first executable line in `__init__`. The key is:
1. Place the `if xpath_format ...` block at the very beginning of the constructor body, before any assignments like `self.data = data`.
2. Ensure the import section already includes `Any` and `list`/`dict` typing (it likely does given your snippet).
</issue_to_address>
### Comment 3
<location> `tests/test_json2xml.py:232-240` </location>
<code_context>
if xmldata:
assert b'encoding="UTF-8"' in xmldata
+
+ def test_xpath_format_basic(self) -> None:
+ """Test XPath 3.1 json-to-xml format with basic types."""
+ data = {"name": "John", "age": 30, "active": True}
+ xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
+ if xmldata:
+ assert b'xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata
+ assert b'<string key="name">John</string>' in xmldata
+ assert b'<number key="age">30</number>' in xmldata
+ assert b'<boolean key="active">true</boolean>' in xmldata
+
+ def test_xpath_format_nested_dict(self) -> None:
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding tests for root-level primitive values (string/number/boolean/null) in XPath format
Current tests cover objects, nested dicts, arrays, and mixed arrays, but not when the root JSON value is a primitive (e.g. `"foo"`, `123`, `true`, `null`). Please add tests for `data = "foo"`, `data = 123`, `data = True/False`, and `data = None` with `xpath_format=True` to verify the expected `<map xmlns=...>` wrapping (or other defined behavior) and lock in namespace handling for root-level primitives.
Suggested implementation:
```python
def test_xpath_format_nested_dict(self) -> None:
"""Test XPath 3.1 format with nested dictionaries."""
data = {"person": {"name": "Alice", "age": 25}}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<map key="person">' in xmldata
assert b'<string key="name">Alice</string>' in xmldata
assert b'<number key="age">25</number>' in xmldata
def test_xpath_format_root_string(self) -> None:
"""Test XPath 3.1 format with a root-level string value."""
data = "foo"
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Root node should be a string in the XPath namespace
assert b'<string xmlns="http://www.w3.org/2005/xpath-functions">foo</string>' in xmldata
def test_xpath_format_root_number(self) -> None:
"""Test XPath 3.1 format with a root-level number value."""
data = 123
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Root node should be a number in the XPath namespace
assert b'<number xmlns="http://www.w3.org/2005/xpath-functions">123</number>' in xmldata
def test_xpath_format_root_boolean(self) -> None:
"""Test XPath 3.1 format with root-level boolean values."""
for value, expected in ((True, b"true"), (False, b"false")):
xmldata = json2xml.Json2xml(value, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Root node should be a boolean in the XPath namespace
assert (
b'<boolean xmlns="http://www.w3.org/2005/xpath-functions">' + expected + b'</boolean>'
in xmldata
)
def test_xpath_format_root_null(self) -> None:
"""Test XPath 3.1 format with a root-level null value."""
data = None
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Root node should be a null in the XPath namespace (likely self-closing)
assert b'<null xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata
def test_xpath_format_array(self) -> None:
"""Test XPath 3.1 format with arrays."""
```
If the actual behavior of `json2xml.Json2xml(..., xpath_format=True)` wraps root-level primitives inside a `<map>` instead of using primitive elements at the root, adjust the assertions accordingly, for example:
- Expect `b'<map xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata`
- Expect child nodes like `b'<string key="value">foo</string>' in xmldata` (or whatever key name is actually used).
Run the test suite once to confirm the exact XML shape and tweak the expected substrings if necessary to match the concrete output (e.g., spacing or self-closing tag format for `<null/>`).
</issue_to_address>
### Comment 4
<location> `tests/test_json2xml.py:261-269` </location>
<code_context>
+ assert b'<number>2</number>' in xmldata
+ assert b'<number>3</number>' in xmldata
+
+ def test_xpath_format_null(self) -> None:
+ """Test XPath 3.1 format with null values."""
+ data = {"value": None}
+ xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
+ if xmldata:
+ assert b'<null key="value"/>' in xmldata
+
+ def test_xpath_format_mixed_array(self) -> None:
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for empty map/array cases in XPath format
Current tests only cover `null` values; please also add cases for empty `{}` and `[]` with `xpath_format=True`. For example, assert that `{}` and `[]` produce `<map>` and `<array>` elements respectively (including the XPath 3.1 namespace at the root, e.g. `<map xmlns="http://www.w3.org/2005/xpath-functions"/>` and the corresponding `<array .../>` / non-root forms) so these empty-container edge cases are verified.
```suggestion
def test_xpath_format_null(self) -> None:
"""Test XPath 3.1 format with null values."""
data = {"value": None}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<null key="value"/>' in xmldata
def test_xpath_format_empty_map_root(self) -> None:
"""Test XPath 3.1 format with an empty map as the root value."""
data: dict = {}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
# Root empty map should be serialized as a bare <map> with the XPath 3.1 namespace
assert xmldata == b'<map xmlns="http://www.w3.org/2005/xpath-functions"/>'
def test_xpath_format_empty_array_root(self) -> None:
"""Test XPath 3.1 format with an empty array as the root value."""
data: list = []
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
# Root empty array should be serialized as a bare <array> with the XPath 3.1 namespace
assert xmldata == b'<array xmlns="http://www.w3.org/2005/xpath-functions"/>'
def test_xpath_format_empty_map_non_root(self) -> None:
"""Test XPath 3.1 format with an empty map as a non-root value."""
data = {"empty_map": {}}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Non-root empty map should be represented as a self-closing <map> with a key attribute
assert b'<map key="empty_map"/>' in xmldata
def test_xpath_format_empty_array_non_root(self) -> None:
"""Test XPath 3.1 format with an empty array as a non-root value."""
data = {"empty_array": []}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Non-root empty array should be represented as a self-closing <array> with a key attribute
assert b'<array key="empty_array"/>' in xmldata
def test_xpath_format_mixed_array(self) -> None:
"""Test XPath 3.1 format with mixed type arrays."""
```
</issue_to_address>
### Comment 5
<location> `tests/test_json2xml.py:279-292` </location>
<code_context>
+ assert b'<boolean>true</boolean>' in xmldata
+ assert b'<null/>' in xmldata
+
+ def test_xpath_format_complex_nested(self) -> None:
+ """Test XPath 3.1 format with complex nested structures."""
+ data = {
+ "content": [
+ {"id": 70805774, "value": "1001", "position": [1004.0, 288.0]},
+ ]
+ }
+ xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
+ if xmldata:
+ assert b'<array key="content">' in xmldata
+ assert b'<number key="id">70805774</number>' in xmldata
+ assert b'<string key="value">1001</string>' in xmldata
+ assert b'<array key="position">' in xmldata
+ assert b'<number>1004.0</number>' in xmldata
+
+ def test_xpath_format_escaping(self) -> None:
</code_context>
<issue_to_address>
**suggestion (testing):** It may be helpful to assert on the root element shape to prove there is no extra wrapper in XPath mode
Since this test currently validates only inner fragments, consider also asserting on the top-level element to confirm the XPath root shape. For example, check that the root is `<map>` with the expected namespace and that no legacy wrapper like `<all>` is present. That will verify `xpath_format=True` affects the root correctly, not just nested nodes.
```suggestion
def test_xpath_format_complex_nested(self) -> None:
"""Test XPath 3.1 format with complex nested structures."""
data = {
"content": [
{"id": 70805774, "value": "1001", "position": [1004.0, 288.0]},
]
}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Assert on the root element shape for XPath mode: a <map> with the XPath namespace
# and no legacy wrapper such as <all>.
assert b'<map xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata
assert b'<all>' not in xmldata
# Existing inner-fragment assertions
assert b'<array key="content">' in xmldata
assert b'<number key="id">70805774</number>' in xmldata
assert b'<string key="value">1001</string>' in xmldata
assert b'<array key="position">' in xmldata
assert b'<number>1004.0</number>' in xmldata
```
</issue_to_address>
### Comment 6
<location> `tests/test_json2xml.py:302-308` </location>
<code_context>
+ assert b"<script>" in xmldata
+ assert b"'xss'" in xmldata
+
+ def test_xpath_format_with_pretty_print(self) -> None:
+ """Test XPath 3.1 format works with pretty printing."""
+ data = {"name": "Test"}
+ xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=True).to_xml()
+ if xmldata:
+ assert 'xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata
+ assert '<string key="name">Test</string>' in xmldata
+
+ def test_xpath_format_root_array(self) -> None:
</code_context>
<issue_to_address>
**issue (bug_risk):** `xmldata` is likely bytes here, so these assertions should consistently work with bytes or decode to str
Earlier in this file `xmldata` is treated as `bytes` (e.g. `b'xmlns=...' in xmldata`), but here the assertions use `str`. If `Json2xml.to_xml()` returns bytes, these will fail on Python 3 due to `str`/`bytes` mismatch. Please either make these assertions use byte literals (e.g. `b'xmlns="..."'`) or decode `xmldata` first (e.g. `xmldata_str = xmldata.decode("utf-8")`) and assert on that, so the pretty-print behavior is actually tested rather than tripping on a type error.
</issue_to_address>
### Comment 7
<location> `tests/test_json2xml.py:294-300` </location>
<code_context>
+ assert b'<array key="position">' in xmldata
+ assert b'<number>1004.0</number>' in xmldata
+
+ def test_xpath_format_escaping(self) -> None:
+ """Test XPath 3.1 format properly escapes special characters."""
+ data = {"text": "<script>alert('xss')</script>"}
+ xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
+ if xmldata:
+ assert b"<script>" in xmldata
+ assert b"'xss'" in xmldata
+
+ def test_xpath_format_with_pretty_print(self) -> None:
</code_context>
<issue_to_address>
**suggestion (testing):** Consider also asserting that unsafe characters are not present unescaped alongside the escaped ones
For example, you could extend this test with negative assertions such as `assert b"<script>" not in xmldata` and `assert b"'xss'" not in xmldata` to verify the raw, unescaped content never appears in the output.
```suggestion
def test_xpath_format_escaping(self) -> None:
"""Test XPath 3.1 format properly escapes special characters."""
data = {"text": "<script>alert('xss')</script>"}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
# Escaped content should be present
assert b"<script>" in xmldata
assert b"'xss'" in xmldata
# Raw, unescaped content must not appear
assert b"<script>" not in xmldata
assert b"'xss'" not in xmldata
```
</issue_to_address>
### Comment 8
<location> `json2xml/dicttoxml.py:196` </location>
<code_context>
+XPATH_FUNCTIONS_NS = "http://www.w3.org/2005/xpath-functions"
+
+
+def get_xpath31_tag_name(val: Any) -> str:
+ """
+ Determine XPath 3.1 tag name by Python type.
</code_context>
<issue_to_address>
**issue (complexity):** Consider removing the unused helper and centralizing XPath 3.1 root/namespace handling so the conversion logic is type-driven instead of string-munging based.
You can simplify this change in two focused spots without altering behavior.
### 1. Remove unused `get_xpath31_tag_name`
`get_xpath31_tag_name` is dead code and duplicates the type-dispatch logic in `convert_to_xpath31`. Removing it reduces cognitive load and keeps a single source of truth for type mapping.
```python
# Remove this entirely – it’s not used and duplicates logic.
def get_xpath31_tag_name(val: Any) -> str:
...
```
If you want to keep a helper, instead refactor `convert_to_xpath31` to *use* it, but as-is the simplest fix is to delete it.
---
### 2. Simplify `xpath_format` root/wrapping logic
The current implementation:
```python
xml_content = convert_to_xpath31(obj)
output = [
'<?xml version="1.0" encoding="UTF-8" ?>',
xml_content.replace("<map", f'<map xmlns="{XPATH_FUNCTIONS_NS}"', 1)
if xml_content.startswith("<map")
else xml_content.replace("<array", f'<array xmlns="{XPATH_FUNCTIONS_NS}"', 1)
if xml_content.startswith("<array")
else f'<map xmlns="{XPATH_FUNCTIONS_NS}">{xml_content}</map>',
]
return "".join(output).encode("utf-8")
```
relies on string-prefix checks and replacement, which is brittle and harder to read.
You can push the namespace handling into `convert_to_xpath31` and use explicit type checks at the call site, keeping behavior the same:
```python
def convert_to_xpath31(obj: Any, parent_key: str | None = None, root: bool = False) -> str:
key_attr = f' key="{escape_xml(parent_key)}"' if parent_key is not None else ""
ns_attr = f' xmlns="{XPATH_FUNCTIONS_NS}"' if root else ""
if obj is None:
return f"<null{key_attr}{ns_attr}/>" if root else f"<null{key_attr}/>"
if isinstance(obj, bool):
return f"<boolean{key_attr}{ns_attr}>{str(obj).lower()}</boolean>"
if isinstance(obj, (int, float, numbers.Number)):
return f"<number{key_attr}{ns_attr}>{obj}</number>"
if isinstance(obj, str):
return f"<string{key_attr}{ns_attr}>{escape_xml(obj)}</string>"
if isinstance(obj, dict):
children = "".join(convert_to_xpath31(v, k) for k, v in obj.items())
return f"<map{key_attr}{ns_attr}>{children}</map>"
if isinstance(obj, Sequence) and not isinstance(obj, str):
children = "".join(convert_to_xpath31(item) for item in obj)
return f"<array{key_attr}{ns_attr}>{children}</array>"
return f"<string{key_attr}{ns_attr}>{escape_xml(str(obj))}</string>"
```
Then `dicttoxml` becomes a straightforward type-based decision without string munging, while preserving your current behavior (root dict/array gets the namespace on that element; scalar roots get wrapped in a `<map>`):
```python
def dicttoxml(..., xpath_format: bool = False) -> bytes:
...
if xpath_format:
# Container roots get the namespace on their own element.
if isinstance(obj, dict) or (isinstance(obj, Sequence) and not isinstance(obj, str)):
xml_body = convert_to_xpath31(obj, root=True)
else:
# Scalars/non-containers: wrap in a map with the namespace, as you do now.
xml_body = (
f'<map xmlns="{XPATH_FUNCTIONS_NS}">'
f'{convert_to_xpath31(obj)}'
f'</map>'
)
return f'<?xml version="1.0" encoding="UTF-8" ?>{xml_body}'.encode("utf-8")
# existing non-xpath_format logic below...
```
This removes nested conditional expressions on strings, makes the root-handling logic explicit and type-driven, and centralizes namespace handling in one place.
</issue_to_address>
### Comment 9
<location> `json2xml/dicttoxml.py:218-219` </location>
<code_context>
def get_xpath31_tag_name(val: Any) -> str:
"""
Determine XPath 3.1 tag name by Python type.
See: https://www.w3.org/TR/xpath-functions-31/#func-json-to-xml
Args:
val: The value to get the tag name for.
Returns:
str: The XPath 3.1 tag name (map, array, string, number, boolean, null).
"""
if val is None:
return "null"
if isinstance(val, bool):
return "boolean"
if isinstance(val, dict):
return "map"
if isinstance(val, (int, float, numbers.Number)):
return "number"
if isinstance(val, Sequence) and not isinstance(val, str):
return "array"
if isinstance(val, str):
return "string"
return "string"
</code_context>
<issue_to_address>
**suggestion (code-quality):** We've found these issues:
- Lift code into else after jump in control flow ([`reintroduce-else`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/reintroduce-else/))
- Hoist repeated code outside conditional statement ([`hoist-statement-from-if`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/hoist-statement-from-if/))
```suggestion
```
</issue_to_address>
### Comment 10
<location> `tests/test_json2xml.py:235-236` </location>
<code_context>
def test_xpath_format_basic(self) -> None:
"""Test XPath 3.1 json-to-xml format with basic types."""
data = {"name": "John", "age": 30, "active": True}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata
assert b'<string key="name">John</string>' in xmldata
assert b'<number key="age">30</number>' in xmldata
assert b'<boolean key="active">true</boolean>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>
### Comment 11
<location> `tests/test_json2xml.py:245-246` </location>
<code_context>
def test_xpath_format_nested_dict(self) -> None:
"""Test XPath 3.1 format with nested dictionaries."""
data = {"person": {"name": "Alice", "age": 25}}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<map key="person">' in xmldata
assert b'<string key="name">Alice</string>' in xmldata
assert b'<number key="age">25</number>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>
### Comment 12
<location> `tests/test_json2xml.py:254-255` </location>
<code_context>
def test_xpath_format_array(self) -> None:
"""Test XPath 3.1 format with arrays."""
data = {"numbers": [1, 2, 3]}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<array key="numbers">' in xmldata
assert b'<number>1</number>' in xmldata
assert b'<number>2</number>' in xmldata
assert b'<number>3</number>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>
### Comment 13
<location> `tests/test_json2xml.py:264-265` </location>
<code_context>
def test_xpath_format_null(self) -> None:
"""Test XPath 3.1 format with null values."""
data = {"value": None}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<null key="value"/>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>
### Comment 14
<location> `tests/test_json2xml.py:271-272` </location>
<code_context>
def test_xpath_format_mixed_array(self) -> None:
"""Test XPath 3.1 format with mixed type arrays."""
data = {"items": ["text", 42, True, None]}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<array key="items">' in xmldata
assert b'<string>text</string>' in xmldata
assert b'<number>42</number>' in xmldata
assert b'<boolean>true</boolean>' in xmldata
assert b'<null/>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>
### Comment 15
<location> `tests/test_json2xml.py:286-287` </location>
<code_context>
def test_xpath_format_complex_nested(self) -> None:
"""Test XPath 3.1 format with complex nested structures."""
data = {
"content": [
{"id": 70805774, "value": "1001", "position": [1004.0, 288.0]},
]
}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<array key="content">' in xmldata
assert b'<number key="id">70805774</number>' in xmldata
assert b'<string key="value">1001</string>' in xmldata
assert b'<array key="position">' in xmldata
assert b'<number>1004.0</number>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>
### Comment 16
<location> `tests/test_json2xml.py:297-298` </location>
<code_context>
def test_xpath_format_escaping(self) -> None:
"""Test XPath 3.1 format properly escapes special characters."""
data = {"text": "<script>alert('xss')</script>"}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b"<script>" in xmldata
assert b"'xss'" in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>
### Comment 17
<location> `tests/test_json2xml.py:305-306` </location>
<code_context>
def test_xpath_format_with_pretty_print(self) -> None:
"""Test XPath 3.1 format works with pretty printing."""
data = {"name": "Test"}
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=True).to_xml()
if xmldata:
assert 'xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata
assert '<string key="name">Test</string>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=True
).to_xml():
```
</issue_to_address>
### Comment 18
<location> `tests/test_json2xml.py:313-314` </location>
<code_context>
def test_xpath_format_root_array(self) -> None:
"""Test XPath 3.1 format with root-level array."""
data = [1, 2, 3]
xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml()
if xmldata:
assert b'<array xmlns="http://www.w3.org/2005/xpath-functions">' in xmldata
assert b'<number>1</number>' in xmldata
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
```suggestion
if xmldata := json2xml.Json2xml(
data, xpath_format=True, pretty=False
).to_xml():
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| key_attr = f' key="{escape_xml(parent_key)}"' if parent_key is not None else "" | ||
|
|
||
| if obj is None: | ||
| return f"<null{key_attr}/>" | ||
|
|
||
| if isinstance(obj, bool): | ||
| return f"<boolean{key_attr}>{str(obj).lower()}</boolean>" | ||
|
|
||
| if isinstance(obj, (int, float, numbers.Number)): | ||
| return f"<number{key_attr}>{obj}</number>" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: Non-string dict keys will raise when building the key attribute instead of being handled gracefully.
In convert_to_xpath31, parent_key is passed directly to escape_xml, which assumes a string. When iterating for k, v in obj.items(), non-string keys (ints, enums, tuples, etc.) will cause a TypeError. To align with the original dicttoxml behavior and keep the function resilient, normalize keys before escaping, e.g.:
key_str = str(parent_key) if parent_key is not None else None
key_attr = f' key="{escape_xml(key_str)}"' if key_str is not None else ""This keeps non-string keys supported while still escaping safely.
| def test_xpath_format_complex_nested(self) -> None: | ||
| """Test XPath 3.1 format with complex nested structures.""" | ||
| data = { | ||
| "content": [ | ||
| {"id": 70805774, "value": "1001", "position": [1004.0, 288.0]}, | ||
| ] | ||
| } | ||
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | ||
| if xmldata: | ||
| assert b'<array key="content">' in xmldata | ||
| assert b'<number key="id">70805774</number>' in xmldata | ||
| assert b'<string key="value">1001</string>' in xmldata | ||
| assert b'<array key="position">' in xmldata | ||
| assert b'<number>1004.0</number>' in xmldata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (testing): It may be helpful to assert on the root element shape to prove there is no extra wrapper in XPath mode
Since this test currently validates only inner fragments, consider also asserting on the top-level element to confirm the XPath root shape. For example, check that the root is <map> with the expected namespace and that no legacy wrapper like <all> is present. That will verify xpath_format=True affects the root correctly, not just nested nodes.
| def test_xpath_format_complex_nested(self) -> None: | |
| """Test XPath 3.1 format with complex nested structures.""" | |
| data = { | |
| "content": [ | |
| {"id": 70805774, "value": "1001", "position": [1004.0, 288.0]}, | |
| ] | |
| } | |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| assert b'<array key="content">' in xmldata | |
| assert b'<number key="id">70805774</number>' in xmldata | |
| assert b'<string key="value">1001</string>' in xmldata | |
| assert b'<array key="position">' in xmldata | |
| assert b'<number>1004.0</number>' in xmldata | |
| def test_xpath_format_complex_nested(self) -> None: | |
| """Test XPath 3.1 format with complex nested structures.""" | |
| data = { | |
| "content": [ | |
| {"id": 70805774, "value": "1001", "position": [1004.0, 288.0]}, | |
| ] | |
| } | |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| # Assert on the root element shape for XPath mode: a <map> with the XPath namespace | |
| # and no legacy wrapper such as <all>. | |
| assert b'<map xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata | |
| assert b'<all>' not in xmldata | |
| # Existing inner-fragment assertions | |
| assert b'<array key="content">' in xmldata | |
| assert b'<number key="id">70805774</number>' in xmldata | |
| assert b'<string key="value">1001</string>' in xmldata | |
| assert b'<array key="position">' in xmldata | |
| assert b'<number>1004.0</number>' in xmldata |
| def test_xpath_format_with_pretty_print(self) -> None: | ||
| """Test XPath 3.1 format works with pretty printing.""" | ||
| data = {"name": "Test"} | ||
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=True).to_xml() | ||
| if xmldata: | ||
| assert 'xmlns="http://www.w3.org/2005/xpath-functions"' in xmldata | ||
| assert '<string key="name">Test</string>' in xmldata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): xmldata is likely bytes here, so these assertions should consistently work with bytes or decode to str
Earlier in this file xmldata is treated as bytes (e.g. b'xmlns=...' in xmldata), but here the assertions use str. If Json2xml.to_xml() returns bytes, these will fail on Python 3 due to str/bytes mismatch. Please either make these assertions use byte literals (e.g. b'xmlns="..."') or decode xmldata first (e.g. xmldata_str = xmldata.decode("utf-8")) and assert on that, so the pretty-print behavior is actually tested rather than tripping on a type error.
| def test_xpath_format_escaping(self) -> None: | ||
| """Test XPath 3.1 format properly escapes special characters.""" | ||
| data = {"text": "<script>alert('xss')</script>"} | ||
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | ||
| if xmldata: | ||
| assert b"<script>" in xmldata | ||
| assert b"'xss'" in xmldata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (testing): Consider also asserting that unsafe characters are not present unescaped alongside the escaped ones
For example, you could extend this test with negative assertions such as assert b"<script>" not in xmldata and assert b"'xss'" not in xmldata to verify the raw, unescaped content never appears in the output.
| def test_xpath_format_escaping(self) -> None: | |
| """Test XPath 3.1 format properly escapes special characters.""" | |
| data = {"text": "<script>alert('xss')</script>"} | |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| assert b"<script>" in xmldata | |
| assert b"'xss'" in xmldata | |
| def test_xpath_format_escaping(self) -> None: | |
| """Test XPath 3.1 format properly escapes special characters.""" | |
| data = {"text": "<script>alert('xss')</script>"} | |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| # Escaped content should be present | |
| assert b"<script>" in xmldata | |
| assert b"'xss'" in xmldata | |
| # Raw, unescaped content must not appear | |
| assert b"<script>" not in xmldata | |
| assert b"'xss'" not in xmldata |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | ||
| if xmldata: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| if xmldata := json2xml.Json2xml( | |
| data, xpath_format=True, pretty=False | |
| ).to_xml(): |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | ||
| if xmldata: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| if xmldata := json2xml.Json2xml( | |
| data, xpath_format=True, pretty=False | |
| ).to_xml(): |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | ||
| if xmldata: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| if xmldata := json2xml.Json2xml( | |
| data, xpath_format=True, pretty=False | |
| ).to_xml(): |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=True).to_xml() | ||
| if xmldata: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=True).to_xml() | |
| if xmldata: | |
| if xmldata := json2xml.Json2xml( | |
| data, xpath_format=True, pretty=True | |
| ).to_xml(): |
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | ||
| if xmldata: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)
| xmldata = json2xml.Json2xml(data, xpath_format=True, pretty=False).to_xml() | |
| if xmldata: | |
| if xmldata := json2xml.Json2xml( | |
| data, xpath_format=True, pretty=False | |
| ).to_xml(): |
|
|
||
| import numbers | ||
| from typing import TYPE_CHECKING | ||
| from unittest.mock import MagicMock, patch |
Check notice
Code scanning / CodeQL
Unused import Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 28 days ago
To fix the flagged issue, we should remove the unused imports of MagicMock and patch from the statement from unittest.mock import MagicMock, patch on line 7. Since neither is used in the code shown, the best course of action is to simply delete this import line altogether. No further edits are required, and removing the line will not impact existing functionality.
| @@ -4,7 +4,6 @@ | ||
|
|
||
| import numbers | ||
| from typing import TYPE_CHECKING | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
|
|
| from typing import TYPE_CHECKING | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest |
Check notice
Code scanning / CodeQL
Unused import Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 28 days ago
To fix the problem, we should delete the unused import statement import pytest on line 9 of tests/test_missing_coverage.py. This change reduces unnecessary dependencies and improves readability. All other imports appear to be used in the code shown, so no further action regarding imports is needed. Remove only the line containing import pytest and leave everything else unchanged.
| @@ -6,7 +6,6 @@ | ||
| from typing import TYPE_CHECKING | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
|
|
||
| from json2xml.dicttoxml import ( | ||
| convert_to_xpath31, |
| from json2xml.dicttoxml import ( | ||
| convert_to_xpath31, | ||
| dicttoxml, | ||
| get_unique_id, | ||
| get_xml_type, | ||
| get_xpath31_tag_name, | ||
| make_id, | ||
| ) |
Check notice
Code scanning / CodeQL
Unused import Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 28 days ago
To fix the problem, the import of make_id in the import statement on line 17 should be removed. We want to modify only the relevant import statement (on line 11–18), taking care not to accidentally remove the rest of the required imports from json2xml.dicttoxml. The best way to fix this is to delete make_id from the tuple of imported objects, while leaving the other imports (convert_to_xpath31, dicttoxml, get_unique_id, get_xml_type, get_xpath31_tag_name) unchanged. No other code or imports need to be modified.
| @@ -14,7 +14,6 @@ | ||
| get_unique_id, | ||
| get_xml_type, | ||
| get_xpath31_tag_name, | ||
| make_id, | ||
| ) | ||
|
|
||
| if TYPE_CHECKING: |
|
@sjehuda Can you please check my branch here and see if that solves your requirements? https://github.com/vinitkumar/json2xml/tree/feature/xpath31-format |
|
Vinit.
Thank you for quickly acting upon this concern.
May I ask to postpone the check to Thursday or Friday?
Also, do you have any special preferences or instruction as to how to
intsall it with "pip" inside "venv", test it, or otherwise?
I will also send a message at the XSLT mailing list and ask someone to
volunteer for that task.
Thank you.
Schimon
…On Mon, 01 Dec 2025 00:36:50 -0800 Vinit Kumar ***@***.***> wrote:
vinitkumar left a comment (vinitkumar/json2xml#259)
@sjehuda Can you please check my branch here and see if that solves
your requirements?
https://github.com/vinitkumar/json2xml/tree/feature/xpath31-format
|
|
Hi @sjehuda # Install from the feature/xpath31-format branch
pip install git+https://github.com/vinitkumar/json2xml.git@feature/xpath31-format
# Install into a virtual environment
python3 -m venv venv
source venv/bin/activate
pip install git+https://github.com/vinitkumar/json2xml.git@feature/xpath31-format
# Install with extras (dev dependencies for testing)
pip install "git+https://github.com/vinitkumar/json2xml.git@feature/xpath31-format#egg=json2xml[dev]"
# Install a specific commit (if you want to pin a version)
pip install git+https://github.com/vinitkumar/json2xml.git@109007efe25cc51e80d62be836ebafa7929357ad
# Install in editable mode (for development)
pip install -e git+https://github.com/vinitkumar/json2xml.git@feature/xpath31-format#egg=json2xml
Then use it immediately: This would give you an idea how it works. Please take any time you want this week. I will only merge and release once the testing has been done for this and is verified to work. |
|
I would want first to try it in accordance with your directives, and then further experiment. May you advise of this issue? |
Sorry for late reply @sjehuda. Here is the updated code you need to run: from json2xml import json2xml
data = {"name": "John", "age": 30}
xml = json2xml.Json2xml(data, xpath_format=True, pretty=True).to_xml()
print(xml)This should return something like: <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.w3.org/2005/xpath-functions">
<string key="name">John</string>
<number key="age">30</number>
</map> |
|
@sjehuda Are you able to test? |
|
Mr. Kumar. Good afternoon. I beg your pardon for this unnecessary delay. Yes. This code works, and to me, it appears to work as expected. As you know, I am a lawyer, not a specialist engineer, so I have asked others to try it as well. https://biglist.com/lists/lists.mulberrytech.com/xsl-list/archives/202512/maillist.html Nevertheless, it seems that the produced result is good, and probably better than my attempt. https://git.xmpp-it.net/sch/Focus/src/branch/main/utilities/json-to-xml.py As I intend to incorporate that functionality into project Slixfeed, I intend to utilize your package "json2xml", not mine, as it would increase probability of reporting of issues, if there are any. https://git.xmpp-it.net/sch/Slixfeed Thank you, very much. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds XPath 3.1 json-to-xml support to the json2xml library, allowing users to generate W3C-compliant XPath 3.1 JSON XML output with the xpath-functions namespace.
Summary
- Adds optional
xpath_formatparameter todicttoxml()andJson2xmlclass - Implements XPath 3.1 json-to-xml conversion following W3C specification
- Updates test dependencies from pinned versions to minimum versions (pytest 7.0.1 → 9.0.1)
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Updated dependency versions with future timestamps indicating potential version issues |
| .coverage | Binary coverage database file that should not be committed |
| pyproject.toml | Version bump to 5.3.1 and test dependency update |
| json2xml/init.py | Version update to 5.3.0 (inconsistent with pyproject.toml) |
| json2xml/json2xml.py | Added xpath_format parameter to Json2xml class |
| json2xml/dicttoxml.py | Core XPath 3.1 implementation with type-based conversion |
| tests/test_json2xml.py | Added comprehensive XPath format tests |
| tests/test_missing_coverage.py | Added tests for edge cases and missing coverage |
| requirements-dev.in | Changed from exact pins to minimum version constraints |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| __author__ = """Vinit Kumar""" | ||
| __email__ = "mail@vinitkumar.me" | ||
| __version__ = "5.2.1" | ||
| __version__ = "5.3.0" |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Version mismatch: The version is set to "5.3.1" in pyproject.toml but "5.3.0" in json2xml/init.py. These should be consistent. Typically, init.py should match pyproject.toml as the single source of truth.
| __version__ = "5.3.0" | |
| __version__ = "5.3.1" |
| [project.optional-dependencies] | ||
| test = [ | ||
| "pytest==7.0.1", | ||
| "pytest>=8.4.1", |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test dependency constraint changed from pytest==7.0.1 (exact pin) to pytest>=8.4.1 (minimum version). This is a significant breaking change as pytest 7.0.1 is very old (2022) and may have different behavior than pytest 9.x. This change should be intentional and all tests should be verified to work with the newer version.
|
|
||
| import numbers | ||
| from typing import TYPE_CHECKING | ||
| from unittest.mock import MagicMock, patch |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'MagicMock' is not used.
| from unittest.mock import MagicMock, patch | |
| from unittest.mock import patch |
| from typing import TYPE_CHECKING | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'pytest' is not used.
| import pytest |
| get_unique_id, | ||
| get_xml_type, | ||
| get_xpath31_tag_name, | ||
| make_id, |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'make_id' is not used.
| make_id, |
|
@sjehuda Good news. I just merged and released a new version of This contains the support for xPath as in docs shared above. You are directly just use https://pypi.org/project/json2xml/ https://json2xml.readthedocs.io/en/latest/readme.html#xpath-3-1-compliance-options Also, will you mind adding the project in the credits of your readme where you want to use it. Totally optional, but just wondering if you would something to promote a project that has been so close to my heart. |
Summary by Sourcery
Add optional XPath 3.1 json-to-xml output mode and wire it through the Json2xml interface.
New Features:
Tests: