Skip to content
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

Strange missing positional arguments error #54

Closed
luni3359 opened this issue May 9, 2022 · 4 comments · Fixed by #56
Closed

Strange missing positional arguments error #54

luni3359 opened this issue May 9, 2022 · 4 comments · Fixed by #56

Comments

@luni3359
Copy link

luni3359 commented May 9, 2022

  • Dataclass Wizard version: 0.22.0
  • Python version: 3.10.4
  • Operating System: Linux

Description

I keep getting errors for some reason, and removing/commenting out the viewMode field creates an instance of ExtendedFetch successfully, but viewMode isn't missing from what it appears.

What I Did

from dataclasses import dataclass
from dataclass_wizard import JSONSerializable

@dataclass
class ExtendedFetch(JSONSerializable):
    deviation: dict
    viewMode: str
    comments: dict
    session: dict

j = '{"deviation": {}, "viewMode": "regular", "comments": {}, "session": {}}'
ExtendedFetch.from_json(j)

Error

dataclass_wizard.errors.MissingFields: Failure calling constructor method of class `ExtendedFetch`. Missing values for required dataclass fields.
  have fields: ['deviation', 'comments', 'session']
  missing fields: ['viewMode']
  input JSON object: {"deviation": {}, "viewMode": "regular", "comments": {}, "session": {}}
  error: ExtendedFetch.__init__() missing 1 required positional argument: 'viewMode'
@rnag
Copy link
Owner

rnag commented May 10, 2022

Update (5/11/22): As of v0.22.1, this issue should now be resolved without additional changes in user code. I am going to keep my original reply below, in case others can find it useful for clarification purposes.


Hi @luni3359! First off, thanks for opening this issue. I agree the documentation can be a bit confusing, so it might be worth updating it to clarify on this particular situation that was brought up. It might also potentially be a good idea to update the error details as displayed above, so that it provides a more clearer message in such a scenario.

To elaborate, the reason in this case is that the default key transform for the load process is to snake case, which is the leading convention in Python. This means that, for example, it tries to populate a dataclass field named my_field, but not myField or MyField.

There are a few common approaches you could use to resolve this.

The first is likely the simplest - we can rename the field to view_mode instead, so that it conforms to Python snake casing, as illustrated below:

from dataclasses import dataclass
from dataclass_wizard import JSONSerializable


@dataclass
class ExtendedFetch(JSONSerializable):
    deviation: dict
    view_mode: str
    comments: dict
    session: dict


j = '{"deviation": {}, "viewMode": "regular", "comments": {}, "session": {}}'

instance = ExtendedFetch.from_json(j)
print(repr(instance))

One other available option, assuming that you really wanted to keep camel casing for the dataclass field names, would be to use a Meta config for the class (the inner class name doesn't matter overmuch, can also be an underscore _ for example) and set the key transform for the load process to camel case.

For example:

from dataclasses import dataclass
from dataclass_wizard import JSONSerializable
from dataclass_wizard.enums import LetterCase


@dataclass
class ExtendedFetch(JSONSerializable):

    class MyMeta(JSONSerializable.Meta):
        # alternatively, this can be defined as a string:
        #   key_transform_with_load = 'CAMEL'
        key_transform_with_load = LetterCase.CAMEL

    deviation: dict
    viewMode: str
    comments: dict
    session: dict


j = '{"deviation": {}, "viewMode": "regular", "comments": {}, "session": {}}'

instance = ExtendedFetch.from_json(j)
print(repr(instance))

Another approach could be to use a custom key mapping to more explicitly map a JSON key to a dataclass field:

from dataclasses import dataclass
# Note: in Python 3.9+, this can be imported from `typing` instead
from typing_extensions import Annotated

from dataclass_wizard import JSONSerializable, json_key


@dataclass
class ExtendedFetch(JSONSerializable):
    deviation: dict
    viewMode: Annotated[str, json_key('viewMode')]
    comments: dict
    session: dict


j = '{"deviation": {}, "viewMode": "regular", "comments": {}, "session": {}}'
instance = ExtendedFetch.from_json(j)
print(repr(instance))

@luni3359
Copy link
Author

Thank you for the explanation! I really appreciate it.

I wasn't really thinking about which casing to adhere with. I'm handling many kinds of json files from different websites so I was just using the field names as they are. I think I'm going to use the second approach for this one case because some fields here are kind of verbose already.

@rnag
Copy link
Owner

rnag commented May 11, 2022

Hi @luni3359, just wanted to share a bit of good news, but as of v0.22.1 the issue as initially noted should now be resolved with no additional changes needed on your end.

You can check out the diff here, but the important thing is that we now do an initial lookup on the very first time to see if a dataclass field exists for the JSON key, and if so then there's no need to call the key transform function at all.

I am going to keep my reply above just for future reference, but also for clarification there should be no changes needed in the user code. In particular, the Meta approach as mentioned above should now not be necessary anymore, as of the latest patch version 🙂

@luni3359
Copy link
Author

This is a pretty good resolution, thank you once again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants