web-dev-qa-db-ja.com

引用符の前にバックスラッシュなしでpythonグラフェンリゾルバーでjsonを返す方法

python(Flask + Graphene)にバックエンドサーバーがあり、次のようなJSONオブジェクトを返す必要があります。

_{
      's1': "Section 1",
      's2': "Section 2",
      's3': "Section 3",
      's4': "Section 4"
}
_

リゾルバは次のようになります。

_ questionnaire = graphene.types.json.JSONString(
        description='JSON result test')

    def resolve_questionnaire(self, info: graphql.ResolveInfo):
        sections = {
          's1': "Section 1",
          's2': "Section 2",
          's3': "Section 3",
          's4': "Section 4"
        }

        print(json.dumps(sections))
        return sections
_

そしてコンソールでは、私が期待するようにprint(json.dumps(sections))の結果として表示されます。

_user-api_1  | {"s1": "Section 1", "s2": "Section 2", "s3": "Section 3", "s4": "Section 4"}
_

しかし、GraphiQLでは、バックスラッシュが付いたすべての引用符が表示されます: enter image description here

_return sections_をreturn json.dumps(sections)に変更すると、次のような結果が得られます。 enter image description here

問題は、グラフェンリゾルバーでJSONオブジェクトを適切に返す方法です?のように使用するjson.replaceメソッドがあることを知っていますhere ですが、私は単にオブジェクトを間違った方法で生成/渡すだけだと思います。

6
John Overiron

あなたの最初の結果

{
  "data": {
    "questionnaire": "{\"s1\": \"Section 1\", \"s2\": \"Section 2\", \"s3\": \"Section 3\", \"s4\": \"Section 4\"}"
  }
}

意図された動作です。結局のところ、questionnaireはJSONStringに解決されます。文字列であるため、二重引用符で囲む必要があります。したがって、内部引用符はエスケープする必要があります。これは JSONの基準 によるものです。

その文字列を使用するには、data.questionnaireオブジェクトで何らかのJSONパーサーを実行する必要があります。たとえば、JavaScriptでは、次のようになります。

var data;
// Fetching logic to get the data object from your GraphQL server
var sections = JSON.parse(data.questionaire);

// Now you can access its objects
console.log(sections.s1) // Should print "Section 1" on the dev console

ただし、sectionsのキーが事前に決定されていない場合(sections.s5定義である可能性があります)、上記の方法は理想的ではありません。 1つのケースですが、undefined別のケースでは)。代わりに、反復できる配列があるかもしれません。これを行うには、明示的なキーと値のペアを持つ「モデル」を定義する必要があります。このようにすることは、GraphQLにも適したフォーマットです。例えば:

import graphene

# Our new model
class Section(graphene.ObjectType):
    key = graphene.String()        # dictionary key
    header = graphene.String()     # dictionary value

# Your previous schema with modifications
class Query(graphene.ObjectType):
    # questionnaire = graphene.types.json.JSONString(description='JSON result test')

    # Return a list of section objects
    questionnaire = graphene.List(Section)

    def resolve_questionnaire(self, info: graphql.ResolveInfo):
        sections = {
          's1': "Section 1",
          's2': "Section 2",
          's3': "Section 3",
          's4': "Section 4"
        }

        sections_as_obj_list = [] # Used to return a list of Section types

        # Create a new Section object for each item and append it to list
        for key, value in sections.items(): # Use sections.iteritems() in Python2
            section = Section(key, value) # Creates a section object where key=key and header=value
            sections_as_obj_list.append(section)

        # return sections
        return sections_as_obj_list

ここで、クエリを実行すると、次のようになります。

query {
  questionnaire {
    key
    header
  }
}

反復可能なJSON配列を返します。

{
  "data" {
    "questionnaire": [
      {
        "key": "s1",
        "header": "Section 1"
      },
      {
        "key": "s2",
        "header": "Section 2"
      },
      {
        "key": "s3",
        "header": "Section 3"
      },
      {
        "key": "s4",
        "header": "Section 4"
      },
    ]
  }
}
9
Tobe E

JSONタイプをサブクラス化して、serializeメソッドを置き換えることができます。

class Any(JSON):
    @staticmethod
    def serialize(dt):
        return dt

その後、代わりに

questionnaire = Field(JSON)

書く

questionnaire = Field(Any)

はい、これは厳密に型指定されたGraphQLの精神を破りますが、それがあなたのやりたいことであるなら、それを行う方法があります。これは出力のみのハックであることに注意してください。引数として任意の構造を受け入れることはできません。

2

Json/dictionaryの配列を返す別のアプローチ。

モデルのミックスインプロパティ属性からjsonを返そうとしていました。

基本的に任意のタイプ(注:入力の利点が失われます)は、辞書のリストからjsonを返すのに役立ちました:

import graphene
from graphene import relay, Scalar
from graphene_Django import DjangoObjectType

class DictType(Scalar):

    @staticmethod
    def serialize(dt):
        return dt

    @staticmethod
    def parse_literal(node):
        return node

    @staticmethod
    def parse_value(value):
        return value

ノード自体は、ミックスインを含むモデルに基づいていました。

class InvoiceFileNode(DjangoObjectType):
    signed_url = graphene.String()
    variable_files = graphene.List(of_type=DictType)

    class Meta:
        model = InvoiceFile
        interfaces = (relay.Node,)
        filter_fields = []
        only_fields = (
           "id",
           "created",
           "signed_url",
           "variable_files",
        )

    def resolve_signed_url(self, *args, **kwargs):
        # @property access from FileMixin
        return self.signed_url

    def resolve_openable_signed_url(self, *args, **kwargs):
        # @property access from FileMixin
        return self.openable_signed_url

次のミックスインは私が返そうとしていたものでしたが、JSONStringof_typeを使用して、辞書をjson文字列にシリアル化しました。

class FileMixin(object):

   @property
   def signed_url(self) -> str:
       return get_signed_url(self.file)

   @property
   def variable_files(self) -> str:
       sizes = []
       for s in [128, 240, 720]:
           item = {"width": s}
           urls = []
           for n in [1,2,3]:
               urls.append(get_sized_url(self.file, n))
           item["urls"] = urls
           sizes.append(item) 
       return sizes       


class InvoiceFile(Models, FileMixin):
    created = DateTimeField()
    file = CharField()


私は次のようなものを返すことに問題がありました:

[{"width": 123, "stuff": [{"more": "stuff"}]}}

返される辞書に何らかの関数やオブジェクトなどが含まれている場合、これはおそらく機能しません。

0
jmunsch