SwissDataScienceCenter / calamus

A JSON-LD Serialization Libary for Python
Apache License 2.0
29 stars 12 forks source link

Serializing a list of nested fields #82

Closed cmdoret closed 1 year ago

cmdoret commented 1 year ago

Hello !

I am trying to serialize a list of nested fields with calamus. In the example below, I use an organization which has (a list of) 2 members.

The code runs if the members are of type field.String, but when I make them a Person, it fails with:

Traceback: ``` Traceback (most recent call last): File "marshmallow/fields.py", line 757, in __init__ self.inner = resolve_field_instance(cls_or_instance) File "marshmallow/utils.py", line 352, in resolve_field_instance raise FieldInstanceResolutionError marshmallow.exceptions.FieldInstanceResolutionError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "nested.py", line 35, in class OrganizationSchema(JsonLDSchema): File "nested.py", line 38, in OrganizationSchema members = fields.List(schema.Person, PersonSchema) File "calamus/fields.py", line 657, in __init__ super().__init__(*args, **kwargs) File "calamus/fields.py", line 128, in __init__ super().__init__(*args, **filtered_kwargs) File "marshmallow/fields.py", line 759, in __init__ raise ValueError( ```
ValueError: The list elements must be a subclass or instance of marshmallow.base.FieldABC.
from dataclasses import dataclass
from typing import List, Optional

from calamus.schema import JsonLDSchema
from calamus import fields
from rdflib.namespace import Namespace

schema = Namespace('http://schema.org/')

@dataclass
class Person:
    _id: str
    first_name: Optional[str] = None

class PersonSchema(JsonLDSchema):
    _id = fields.Id()
    first_name = fields.String(schema.givenName)

    class Meta:
        rdf_type = schema.Person
        model = Person

@dataclass
class Organization:
    _id: str
    name: str
    members: Optional[List[Person]] = None

class OrganizationSchema(JsonLDSchema):
    _id = fields.Id()
    name = fields.String(schema.name)
    members = fields.List(schema.member, PersonSchema)

    class Meta:
        rdf_type = schema.Organization
        model = Organization

people = [Person("X", "John"), Person("Y", "Max")]
org = Organization("SO", "SomeOrg", people)
print(OrganizationSchema().dumps(org, indent=2))

I would have expected the following output instead:

{
  "http://schema.org/member": [
    {
      "@id": "X",
      "@type": [
        "http://schema.org/Person"
      ],
      "http://schema.org/givenName": "John"
    },
    {
      "@id": "Y",
      "@type": [
        "http://schema.org/Person"
      ],
      "http://schema.org/givenName": "Max"
    }
  ],
  "http://schema.org/name": "SomeOrg",
  "@id": "SO",
  "@type": [
    "http://schema.org/Organization"
  ]
}

Is there currently a recommended way to do this with calamus ?

Env: python 3.10.6, calamus 0.4.1

cmdoret commented 1 year ago

I found the fix, the fields.Nested with many=True seems to do that, it was just not documented.

class OrganizationSchema(JsonLDSchema):
    _id = fields.Id()
    name = fields.String(schema.name)
    members = fields.Nested(schema.member, PersonSchema, many=True)

    class Meta:
        rdf_type = schema.Organization
        model = Organization

output:

{
  "http://schema.org/member": [
    {
      "http://schema.org/givenName": "John",
      "@id": "X",
      "@type": [
        "http://schema.org/Person"
      ]
    },
    {
      "http://schema.org/givenName": "Max",
      "@id": "Y",
      "@type": [
        "http://schema.org/Person"
      ]
    }
  ],
  "http://schema.org/name": "SomeOrg",
  "@id": "SO",
  "@type": [
    "http://schema.org/Organization"
  ]
}