Skip to content

Commit

Permalink
fix: support deserialization of XML flat arrays where child_name do…
Browse files Browse the repository at this point in the history
…es not conform to current formatter #89 (#90)

Signed-off-by: Paul Horton <[email protected]>
  • Loading branch information
madpah authored Apr 4, 2024
1 parent 1577157 commit ade5bd7
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 7 deletions.
8 changes: 8 additions & 0 deletions serializable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,14 @@ def strip_default_namespace(s: str) -> str:
# Handle Sub-Elements
for child_e in data:
decoded_k = CurrentFormatter.formatter.decode(strip_default_namespace(child_e.tag))

if decoded_k not in klass_properties:
for p, pi in klass_properties.items():
if pi.xml_array_config:
array_type, nested_name = pi.xml_array_config
if nested_name == strip_default_namespace(child_e.tag):
decoded_k = p

if decoded_k in klass.ignore_during_deserialization:
_logger.debug('Ignoring %s when deserializing %s.%s', decoded_k, cls.__module__, cls.__qualname__)
continue
Expand Down
4 changes: 3 additions & 1 deletion tests/fixtures/the-phoenix-project-camel-case-1-v4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@
<reference ref="my-ref-1" />
</references>
<rating>9.8</rating>
</book>
<stockId>stock-id-1</stockId>
<stockId>stock-id-2</stockId>
</book>
6 changes: 5 additions & 1 deletion tests/fixtures/the-phoenix-project-camel-case-v4.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"number": 5,
"name": "5th Anniversary Limited Edition"
},
"stockIds": [
"stock-id-1",
"stock-id-2"
],
"publishDate": "2018-04-16",
"type": "fiction",
"authors": [
Expand Down Expand Up @@ -53,4 +57,4 @@
"reference": "my-ref-1"
}
]
}
}
4 changes: 3 additions & 1 deletion tests/fixtures/the-phoenix-project-defaultNS-isset-v4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@
</chapter>
</chapters>
<rating>9.8</rating>
</book>
<stockId>stock-id-1</stockId>
<stockId>stock-id-2</stockId>
</book>
4 changes: 3 additions & 1 deletion tests/fixtures/the-phoenix-project-defaultNS-mixed-v4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@
</my:chapter>
</my:chapters>
<my:rating>9.8</my:rating>
</my:book>
<my:stockId>stock-id-1</my:stockId>
<my:stockId>stock-id-2</my:stockId>
</my:book>
4 changes: 3 additions & 1 deletion tests/fixtures/the-phoenix-project-defaultNS-unset-v4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@
</my:chapter>
</my:chapters>
<my:rating>9.8</my:rating>
</my:book>
<my:stockId>stock-id-1</my:stockId>
<my:stockId>stock-id-2</my:stockId>
</my:book>
62 changes: 60 additions & 2 deletions tests/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,54 @@ def __repr__(self) -> str:
return f'<BookReference ref={self.ref}, targets={len(self.references)}>'


@serializable.serializable_class
class StockId(serializable.helpers.BaseHelper):

def __init__(self, id: str) -> None:
self._id = id

@property
@serializable.json_name('.')
@serializable.xml_name('.')
def id(self) -> str:
return self._id

@classmethod
def serialize(cls, o: Any) -> str:
if isinstance(o, StockId):
return str(o)
raise Exception(
f'Attempt to serialize a non-StockId: {o!r}')

@classmethod
def deserialize(cls, o: Any) -> 'StockId':
try:
return StockId(id=str(o))
except ValueError as err:
raise Exception(
f'StockId string supplied does not parse: {o!r}'
) from err

def __eq__(self, other: Any) -> bool:
if isinstance(other, StockId):
return hash(other) == hash(self)
return False

def __lt__(self, other: Any) -> bool:
if isinstance(other, StockId):
return self._id < other._id
return NotImplemented

def __hash__(self) -> int:
return hash(self._id)

def __repr__(self) -> str:
return f'<StockId {self._id}>'

def __str__(self) -> str:
return self._id


@serializable.serializable_class(name='bigbook',
ignore_during_deserialization=['something_to_be_ignored', 'ignore_me', 'ignored'])
class Book:
Expand All @@ -234,7 +282,7 @@ def __init__(self, title: str, isbn: str, publish_date: date, authors: Iterable[
publisher: Optional[Publisher] = None, chapters: Optional[Iterable[Chapter]] = None,
edition: Optional[BookEdition] = None, type: BookType = BookType.FICTION,
id: Optional[UUID] = None, references: Optional[Iterable[BookReference]] = None,
rating: Optional[Decimal] = None) -> None:
rating: Optional[Decimal] = None, stock_ids: Optional[Iterable[StockId]] = None) -> None:
self._id = id or uuid4()
self._title = title
self._isbn = isbn
Expand All @@ -246,6 +294,7 @@ def __init__(self, title: str, isbn: str, publish_date: date, authors: Iterable[
self._type = type
self.references = set(references or [])
self.rating = Decimal('NaN') if rating is None else rating
self._stock_ids = set(stock_ids or [])

@property
@serializable.xml_sequence(1)
Expand Down Expand Up @@ -322,6 +371,13 @@ def rating(self) -> Decimal:
def rating(self, rating: Decimal) -> None:
self._rating = rating

@property
@serializable.view(SchemaVersion4)
@serializable.xml_array(XmlArraySerializationType.FLAT, 'stockId')
@serializable.xml_sequence(21)
def stock_ids(self) -> Set[StockId]:
return self._stock_ids


ThePhoenixProject_v1 = Book(
title='The Phoenix Project', isbn='978-1942788294', publish_date=date(year=2018, month=4, day=16),
Expand All @@ -343,7 +399,8 @@ def rating(self, rating: Decimal) -> None:
publisher=Publisher(name='IT Revolution Press LLC', address='10 Downing Street'),
edition=BookEdition(number=5, name='5th Anniversary Limited Edition'),
id=UUID('f3758bf0-0ff7-4366-a5e5-c209d4352b2d'),
rating=Decimal('9.8')
rating=Decimal('9.8'),
stock_ids=[StockId('stock-id-1'), StockId('stock-id-2')]
)

ThePhoenixProject_v2.chapters.append(Chapter(number=1, title='Tuesday, September 2'))
Expand All @@ -370,6 +427,7 @@ def rating(self, rating: Decimal) -> None:

import io
import json

tpp_from_xml = ThePhoenixProject.from_xml( # type:ignore[attr-defined]
io.StringIO(tpp_as_xml))
tpp_from_json = ThePhoenixProject.from_json( # type:ignore[attr-defined]
Expand Down

0 comments on commit ade5bd7

Please sign in to comment.