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では、バックスラッシュが付いたすべての引用符が表示されます:
_return sections
_をreturn json.dumps(sections)
に変更すると、次のような結果が得られます。
問題は、グラフェンリゾルバーでJSONオブジェクトを適切に返す方法です?のように使用するjson.replaceメソッドがあることを知っていますhere ですが、私は単にオブジェクトを間違った方法で生成/渡すだけだと思います。
あなたの最初の結果
{
"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"
},
]
}
}
JSONタイプをサブクラス化して、serializeメソッドを置き換えることができます。
class Any(JSON):
@staticmethod
def serialize(dt):
return dt
その後、代わりに
questionnaire = Field(JSON)
書く
questionnaire = Field(Any)
はい、これは厳密に型指定されたGraphQLの精神を破りますが、それがあなたのやりたいことであるなら、それを行う方法があります。これは出力のみのハックであることに注意してください。引数として任意の構造を受け入れることはできません。
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
次のミックスインは私が返そうとしていたものでしたが、JSONString
のof_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"}]}}
注
返される辞書に何らかの関数やオブジェクトなどが含まれている場合、これはおそらく機能しません。