0

我有以下具有自定义属性的数据模型:

    class Foo {
      private Long id;
      private Set<AdditionalAttribute> attributes;
    }
    class AdditionalAttribute {
      private Key key;
      private String value;
    }
    class Key {
        private String name;
        private Class<?> type;
    }

我的模型产生这个 json:

{"id":123, "attributes": [{"key1":12345}, {"key2":"value2"}]}

我预期的json是:

{"id":123, "key1":12345, "key2":"value2"}

如何使用 graphql spqr 实现这样的序列化/反序列化?

仅供参考,目前我可以在 REST API 中使用 jackson(用于序列化的 BeanSerializerModifier 和用于反序列化的 @JsonAnySetter)如下:

    // Serialization using BeanSerializerModifier
    class FooModifier extends BeanSerializerModifier {

        @Override
        public List<BeanPropertyWriter> changeProperties(
              SerializationConfig config, BeanDescription beanDesc,
              List<BeanPropertyWriter> beanProperties) {
            for (int i = 0; i < beanProperties.size(); i++) {
                BeanPropertyWriter writer = beanProperties.get(i);
                if (Foo.class.isAssignableFrom(beanDesc.getBeanClass()) && "attributes".equals(writer.getName())) {
                    beanProperties.set(i, new FooAttributesWriter(writer));
                }
            }
            return beanProperties;
        }
    }
    class FooAttributesWriter extends BeanPropertyWriter {
        public HasAttributesWriter(BeanPropertyWriter w) {
            super(w);
        }
        @Override
        public void serializeAsField(Object bean, JsonGenerator gen,
                                     SerializerProvider prov) throws Exception {
            if(Foo.class.isAssignableFrom(bean.getClass())) {
                Set<AdditionalAttribute> set = ((Foo) bean).getAttributes();
                for (AdditionalAttribute a : set) {
                    gen.writeStringField(a.getKey().getName(), a.getValue());
                }
            }
        }
    }

    // Deserilization using @JsonAnySetter
    class Foo {
        private Long id;
        private Set<AdditionalAttribute> attributes;
        // Deserialization of custom properties
        @JsonAnySetter
        public void set(String name, Object value) {
            attributes.add(new AdditionalAttribute(buildKey(name,value), value));
        }
    }
4

2 回答 2

2

The problem here is not JSON (de)serialization. With GraphQL, the shape of all your inputs and outputs is defined by the schema, and the schema can not normally have dynamic parts (object types where the fields are unknown ahead of time). Because your Set<AdditionalAttribute> can contain anything at all at runtime, it means your Foo type would have to have unknown fields. This is highly antithetical to how GraphQL is designed.

The only way to achieve a dynamic structure is to have an object scalar which effectively is a JSON blob that can not be validated, or sub-selected from. You could turn Foo into such a scalar by adding @GraphQLScalar to it. Then all input would be valid, {"id":123, "key1":12345 "key2":"value2"} but also {"whatever": "something"}. And it would be your logic's job to ensure correctness. Additionally, if you ever return Foo, the client would not be able to sub-select from it. E.g. {foo} would be possible but {foo { id }} would not, because the schema would no longer know if the id field is present.

To recap, you options are:

  1. Leaving it as it is (the dynamic stuff is a list nested under attributes)
  2. Turning Set<AdditionalAttribute> into a type (a new class or EnumMap) with known structure with all the possible keys as fields. This is only possible if the keys aren't totally dynamic
  3. Making the whole enclosing object an object scalar by using @GraphQLScalar
于 2019-10-21T09:16:11.367 回答
1

非常感谢您的时间和建议的选项。目前,我们找到了另一种方法(可能是选项 4 :))来生成与预期输出“相似”的 json(我们在生成的输出中丢失了类型信息,但我们有另一个逻辑可以帮助我们检索类型)。

这里有一个例子:

class Foo {
      private Long id;
      private Set<AdditionalAttribute> attributes;
      @GraphQLQuery
      public String get(@GraphQLArgument(name = "key") String key) {
        for (AdditionalAttribute a : attributes) {
            if (a.getConfigurationKey().getKey().equalsIgnoreCase(key)) {
                return a.getAttributeValue();
            }
        }
        return null;
    }

我们可以子选择 Foo 如下:

foo {
   id
   key1: get(key: "key1")
   key2: get(key: "key2")
}

而这个回报

{"id":123, "key1":"12345", "key2":"value2"}
于 2019-10-22T11:47:53.950 回答