如何在 python 石墨烯解析器中 return json 引号前没有反斜杠

How to return json in python graphene resolver without backslashes before quotation marks

我在 python(Flask + Graphene)中有一个后端服务器,我需要 return 一个 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) 时,我得到的结果如下:

问题是如何在石墨烯解析器中正确地return一个JSON对象? 我知道有 json.replace 方法可以像 一样使用,但我相信我只是 producing/passing 以错误的方式使用对象。

你的初步结果

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

是预期的行为。毕竟,questionnaire 解析为 JSONString。由于它是一个字符串,因此必须用双引号引起来,因此必须转义其内部引号。这是根据 JSON's standards.

要使用该字符串,您必须在 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在一种情况下可能定义undefined 在另一个)。相反,您可能更愿意拥有一个可以迭代的数组。为此,您必须定义一个具有显式键值对的 "model"。这样做也是适合 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
  }
}

它 returns 一个 JSON 可以迭代的数组。

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

您可以将 JSON 类型子类化并替换序列化方法:

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

然后代替

questionnaire = Field(JSON)

questionnaire = Field(Any)

是的,这确实打破了 GraphQL 的严格类型精神,但如果那是你想做的,那就有办法了。请注意,这是一个仅输出的 hack——它不允许您接受任意结构作为参数。

另一种方法,return一个json/字典的数组。

我试图从模型混合 属性 属性中 return json。

基本上是任何类型(注意:失去打字的好处),帮助我从字典列表中 return 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

下面的 mixin 是我试图得到的 returned,但是使用 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()


我遇到了 return 类似的问题:

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

注意

如果字典 returned 包含任何类型的函数或对象等,这可能不起作用

在我的例子中,我有一个名为(详细信息)的 JSON 列。

from graphene.types.scalars import Scalar

class ObjectField(Scalar):
   ''' convert the Json String into Json '''
   @staticmethod
   def serialize(dt):
      return dt

   @staticmethod
   def parse_literal(node):
      return node.value

   @staticmethod
   def parse_value(value):
      return value


class CustomDiseaseFactNode(graphene.Node):
    class Meta:
       name = 'diseaseFactNode'
    @staticmethod #this class used to get the primary key object id
    def to_global_id(type, id):
      return id

Call the JSONScalar from your object class

class DiseaseFactNode(SQLAlchemyObjectType):
"""DiseaseFact node."""
class Meta:
    model = DiseaseFact
    interfaces = (CustomDiseaseFactNode,)

details = JSONScalar()

石墨烯现在有一个 GenericScalar 类型。

from graphene.types import generic

...
errors = generic.GenericScalar()