Skip to main content

streaming parser using gjson-style path queries over XML

Project description

pygxml

streaming parser using gjson-style path queries over XML. Rust core (quick-xml) + PyO3.

The original GJSON: tidwall/gjson

Installation

pip install pygxml

Usage examples

import pygxml

xml = b"""<store>
  <book id="b1"><title>XML in a Nutshell</title><price>30</price></book>
  <book id="b2"><title>The Cathedral and the Bazaar</title><price>20</price></book>
  <book id="b3"><title>Programming Rust</title><price>45</price></book>
</store>"""

# Single-shot path query — returns a typed Result.
pygxml.get(xml, "store.book").type_                         # list (3 books)
pygxml.get(xml, "store.book.0.title").to_str()             # 'XML in a Nutshell'
pygxml.get(xml, "store.book.1.@id").to_str()               # 'b2'
pygxml.get(xml, "store.book.#").to_int()                   # 3
[str(r) for r in pygxml.get(xml, "store.book.#.title")]    # ['XML in a Nutshell', ...]

# A bare child name with multiple matches AND a follow-on step is rejected:
# the user must pick `.N` (single) or `.#` (each).
pygxml.get(xml, "store.book.title")                         # ValueError

# Filters
pygxml.get(xml, "store.book.#(price>=30).title").to_str()           # 'XML in a Nutshell'
[str(r) for r in pygxml.get(xml, "store.book.#(price>=30)#.title")] # all matches
pygxml.get(xml, 'store.book.#(@id=="b2").title').to_str()           # 'The Cathedral...'

# Modifiers
pygxml.get(xml, "store.book.#.title|@count").to_int()               # 3

# Result.get(...) — descend into a captured element fragment.
book = pygxml.get(xml, "store.book.0")
book.get("title").to_str()                                # 'XML in a Nutshell'
book.get("@id").to_str()                                  # 'b1'
book.get("price").to_int()                                # 30

# parse(data) — wrap the input as a top-level Result for chained navigation.
r = pygxml.parse(xml)
r.get("store.book.0.title").to_str()                      # 'XML in a Nutshell'
r.get("store.book.#(price>=30)#.title").value             # [Result('XML in a Nutshell'), Result('Programming Rust')]

# get_many — scan the document once and return multiple Results.
title, price = pygxml.get_many(xml, ["store.book.0.title", "store.book.0.price"])

# mmap input — true zero-copy on huge files. parse(mm) keeps the mmap by
# reference, so subsequent .get() calls re-borrow it without copying.
import mmap
with open('huge.xml', 'rb') as f, mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
    title = pygxml.parse(mm).get("store.book.0.title").to_str()

# Namespace prefix-aware match
pygxml.get(atom_xml, "atom:feed.atom:entry.atom:title").to_str()

# Validate well-formedness without raising.
pygxml.validate(xml)                                        # True

API

Module-level functions

Function Description
get(xml, path) Query xml (str) at path; returns Result
get_bytes(xml, path) Query xml (bytes) at path; returns Result
get_buffer(xml, path) Query xml (buffer protocol) at path; returns Result
get_many(xml, paths) Query xml (str) at each path; returns list[Result]
get_many_bytes(xml, paths) Query xml (bytes) at each path; returns list[Result]
get_many_buffer(xml, paths) Query xml (buffer protocol) at each path; returns list[Result]
parse(xml) Parse the entire JSON document into a Result
validate(xml) True if xml is syntactically valid

Result

get and parse return a Result. Result accessors

Properties

Property Description
r.type_ Python type for this value: None, bool, int, float, str, list, dict
r.value Value converted to the corresponding Python type: None / int / float / str / list[Result] / dict[str, Result]

gjson-style methods

Method Description
r.exists() True if the value was found in the XML
r.to_str() String representation (text content for elements, or full XML for dict/list elements)
r.to_int() / r.to_float() Typed coercions; return 0 / 0.0 when empty
r.to_bool() gjson-style boolean coercion (see below); returns False when empty
r.get(path) Sub-query relative to this value
r.get_many(paths) Sub-query at multiple paths; returns list[Result]

Result.to_bool() follows gjson semantics:

  • "1" / "true"True; "0" / "false"False
  • "\"t\"" / "\"T\"" / "\"1\""True; "\"f\"" / "\"F\"" / "\"0\""False
  • "\"true\"" / "\"TRUE\"" / "\"True\""True; "\"false\"" / "\"FALSE\"" / "\"False\""False
  • Any other value: to_int() != 0 (non-numeric strings → False)
  • Non-empty dict or list Result → True; empty Result → False

Result.get(path) only descends into element items — scalar items (attributes, #text, counts, modifier aggregates like @sum) have no children, so .get(...) against them yields an empty Result.

Pythonic methods

Syntax Description
str(v),repr(v) dict: <Result type=dict, keys=[...]>; list: <Result type=list, value=[...]>; others: str(v.value)
int(v) 64-bit Integer
float(v) 64-bit float
bool(v) Equivalent to bool(v.value)False for null/false/0/""/[]/{}
len(v) Chars for String; element count for list/dict elements
v[key] Subscript access
key in v Key membership for dict; string match for list
iter(v) Lazy iterator: chars for str; Results for list; keys for dict
v.keys() Lazy KeysView of dict keys (raises TypeError for non-dict)
v.values() Lazy ValuesView of dict values (raises TypeError for non-dict)
v.items() Lazy ItemsView of (key, Result) pairs (raises TypeError for non-dict)
r == "x", r == ["a", "b"], r == other_result Equality with str/list/Result

Path syntax

syntax meaning
a.b.c Descend into child elements (local-name match, ignores namespaces)
a.0, a.1 N-th same-named sibling
a.# Count of same-named siblings
a.#.b Project b over all same-named siblings
*, ? Wildcards in element name
@name Attribute reference
#text Explicit text content
\., \@ Escape
a.#(expr) Filter, first match. expr ::= path op value
a.#(expr)# Filter, all matches
path | @modifier Apply modifier (@reverse, @first, @last, @count, @sort, @sort_n, @unique/@uniq, @flatten, @tostr, @sum, @avg/@mean, @min, @max)
prefix:local Prefix-aware match — qualified-name literal compare (matches <atom:title>, not <rss:title>)
a.**.b Descendant: match every b at any depth under a (XPath // equivalent)

A bare child name (no .N/.#/filter) is implicit: at the terminal position it returns every match (a list-shaped Result), but at a non-terminal position the engine raises ValueError if more than one element matches. To chain past a multi-match step, pick one (.0, .1, …) or project explicitly with .#. Single-match elements (e.g., a unique root element) chain transparently.

Result.get(path) follows the same rule: it requires the receiver to hold at most one element. To process every element of a multi-match Result, iterate (for item in result: item.get(...)).

Filter operators: == = != < <= > >= % (glob) !% (negative glob). Filter values: "string", number, true/false, or bare unquoted text.

Inputs

bytes, str, mmap.mmap, bytearray, memoryview — anything implementing the buffer protocol. bytes and mmap are zero-copy on the way in; str is copied once to UTF-8.

pygxml.parse(data) keeps a reference to the input object instead of copying it. Two consequences worth knowing:

  • For mmap input, do not close the mmap while a Result derived from it is still in use — re-borrows during .get() / .str() will fault.
  • For bytearray and other mutable buffers, mutations after parse(...) are observed by subsequent Result accesses.

pygxml.get(data, path) does not retain the input; captured element fragments inside the returned Result are owned copies.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pygxml-0.0.1.tar.gz (65.7 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pygxml-0.0.1-cp311-abi3-win_arm64.whl (420.7 kB view details)

Uploaded CPython 3.11+Windows ARM64

pygxml-0.0.1-cp311-abi3-win_amd64.whl (437.3 kB view details)

Uploaded CPython 3.11+Windows x86-64

pygxml-0.0.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (564.3 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

pygxml-0.0.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (557.1 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

pygxml-0.0.1-cp311-abi3-macosx_11_0_arm64.whl (524.1 kB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

pygxml-0.0.1-cp311-abi3-macosx_10_12_x86_64.whl (537.1 kB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file pygxml-0.0.1.tar.gz.

File metadata

  • Download URL: pygxml-0.0.1.tar.gz
  • Upload date:
  • Size: 65.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pygxml-0.0.1.tar.gz
Algorithm Hash digest
SHA256 7d5f1dc0d156256cc0a51f62c8cda16e632877df222f319e81607cae5b4c447e
MD5 808a727e67ba074591186eb4205a81ca
BLAKE2b-256 c67b6c184775b9e9a6d43562e1098dcd7d8b98f1d137fcf1082734e15084f8a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.1.tar.gz:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.1-cp311-abi3-win_arm64.whl.

File metadata

  • Download URL: pygxml-0.0.1-cp311-abi3-win_arm64.whl
  • Upload date:
  • Size: 420.7 kB
  • Tags: CPython 3.11+, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pygxml-0.0.1-cp311-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 5701bb5741b1eddee24ce96f45a3c1b05ccae82d3ac77578e22deb00a96bb43f
MD5 a5f7b90ea1bf4c050d7d962141e46670
BLAKE2b-256 07e76c7bf900b38f356213471b67825664c08e06ea7f3d8e5ff8373630249405

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.1-cp311-abi3-win_arm64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.1-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: pygxml-0.0.1-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 437.3 kB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pygxml-0.0.1-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 9e9942536cbf18c0a504506ba95a213518d2d8c2a27f8f154fd56f8a174b9bc7
MD5 854f41077b400226fbeaac7e7f970284
BLAKE2b-256 5153dc62a7d78fad87bb868b29e149cc28b120a99fdbb509d2d9530368cd6790

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.1-cp311-abi3-win_amd64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4e1d0245d67216ba8a10d5b748a48b2fc98c00c574b1ad71da9cba145eeb609d
MD5 26a748a1a3f935f7900a3f99303a8327
BLAKE2b-256 ece5252fbd4b597e17f2e345656b92dd1fc4335e9bad05d73989ed010e541d80

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 816b8f878f2636f81877afdb5721ef88c48f9b3363d169d731111bea1d4bbd9d
MD5 c891c685abc32ac9ed0146b347874e4b
BLAKE2b-256 f9eaf76e6b3c978b45330a8dcdf5bd136973c06a31bff0dbb1ae1fe6d070f232

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.1-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.1-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.1-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8cb5f01603ed54d482c1dae1f3cac926ac44900e5bb285478d884b4da16f57c7
MD5 0edd50d8866794bb0242346e4b7da19c
BLAKE2b-256 41ad8dcd102f3122baaf4c7276093e3ad76fb185dc58ba73565cf3e5decfac20

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.1-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.1-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.1-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d27fcfd128d25156d131f9d6495bf454bb59c75865df3f7fa663f98d8ed30f22
MD5 72b6bfcac9d0a03a6289ba2f144db8cc
BLAKE2b-256 8b7b784e722706a02f5198e7491e6920913c77e9222af73810c9ff26c08e9204

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.1-cp311-abi3-macosx_10_12_x86_64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page