Json to GraphQLArgumetn object conversion failing in graphql-spqr.
tried adding GraphQLInterface(with autodiscovery true and scanpackage) to above abstract classes
and GraphQLtype type all concrete classes.
My graph query:
query contactsQuery($searchQuery : QueryInput) { contacts(searchQuery:$searchQuery){id}}
variables:{"searchQuery":{"bool":{"conditions":[{"must":{"matches":[{"singleFieldMatch":{"boost":null,"field":"firstname","value":"siddiq"}}],"bool":null}}]}})
Java code:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({#type(value = Must.class, name="must"),#type(value = MustNot.class, name="mustNot")})
public abstract class Condition
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({#type(value = SingleFieldMatch.class, name="singleFieldMatch"),#type(value = MultiFieldMatch.class, name="multiFieldMatch")})
public abstract class Match
#GraphQLQuery(name = "contacts")
public List getContacts(#GraphQLArgument(name ="searchQuery") Query query)
Still it's throwing error unknown field error etc. Not sure which configuration is missing.
Building GraphQLSchema with AnnotatedResolvedBuilder, base package configured JacksonValueMappperFactory and singleton services.
Hi this may be a similar issue to what I ended up having.
Initially I had the following
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#GraphQLInterface(name = "AbstractClass", implementationAutoDiscovery = true)
public abstract class AbstractClass{
with the following query called
addNewObject(object: {name: "soft2", id: "asdas"})
To get conversion functioning what I needed to do was the following change
#JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type")
#GraphQLInterface(name = "AbstractClass", implementationAutoDiscovery = true)
public abstract class AbstractClass{
private String type = this.getClass().getSimpleName();
/**
* #return the type
*/
#GraphQLQuery(name = "type", description = "The concrete type of the node. This should match the initialised class. E.g. \"Concrete\", \"DecafCoffee\"")
public String getType() {
return type;
}
with the query now being
addNewConcreteObject(concrete: {name: "soft2", id: "asdas", type: "Concrete"})
Why this worked (I think):
When converting from JSON to objects in my code using the Jackson converter (ObjectMapper). I had previously noticed that the JSON required knowledge of what class to convert to. Thus the initial use of #JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type") put a type property in the JSON when it was written to string.
The inclusion of the #JSON tag may be picked up by SPQR and it then seems to use a Jackson converter to try to convert your query to the required object.
If I am right, here is the issue.
As the query doesn't contain type the query can not be correctly converted. Moreover as the type property was not a member variable of the object but was instead only added by the ObjectMapper, SPQR didn't pick it up and so it wasn't part of the schema for the object. Thus to get around it, I added type as a member variable which is always equal to the actual class, then changed my JsonTypeInfo to look for an existing property.
I appreciate this isn't a direct answer to your question (and definitely isn't a pretty answer), but hopefully it will help you find your solution.
Related
I have a JSON response coming from an API that is calling Elastic Search. The following is a sample snippet.
{
"fielda" : "something",
"hits" : {
"total" : "100",
"type" : "/some/type"
"dynamicAttributes":{
"somevalue1" : "somevalue",
"somevalue2" : "somevalue2"
}
}
}
The JSON subtree underneath the dynamicAttributes can be different with each result. I am trying to marshal that into an object based on the type field that comes in. There will be an established mapping between that value and a class somewhere in a map. It looks like Jackson maps the results into a LinkedHashMap if the type is not resolved. I'm looking to use a Custom Deserializer. Is this the best strategy for this? Or is there something simpler that I'm missing.
You can use #JsonTypeInfo and #JsonSubType annotations to marshal the given input.
On your dynamicAttributes field you can add a JsonTypeInfo annotation to specify how to create a dynamicAttribute's object. The EXTERNAL_PROPERTY type conveys that the type field is present at the level of dynamicAttributes.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property ="type", include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
visible = true)
DynamicAttributes dynamicAttributes;
To specify the possible subtypes use the JsonSubTypes annotation as shown below
#JsonSubTypes({
#JsonSubTypes.Type(value = DynamicAttributesType1.class, name = "type1")
, #JsonSubTypes.Type(value = DynamicAttributesType2.class, name = "type2")
})
public abstract class DynamicAttributes{
}
Let's say I have the following classes:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type",
visible = true
)
#JsonSubTypes(
JsonSubTypes.Type(FullXResult::class, name = "X")
)
open class BaseResult {
protected val mapper: ObjectMapper = ObjectMapper()
var x: String = "some shared text between all subclasses"
}
open class MinimalXResult: BaseResult() {
var y: String = "some public text specific to X"
}
class FullXResult: MinimalXResult() {
var z: String = "some private text specific to X"
fun asMinimal(): MinimalXResult {
return mapper.convertValue(this, MinimalXResult::class.java)
}
}
I want to be able to deserialize a JSON into the FullResult, and before re-serialization, cast it to a MinimalResult to the caller such that for the example classes, the properties x and y are serialized but the property z is not. #JsonIgnore is not an option here because I also want to be able to return the full result under certain conditions.
When calling asMinimal function, I get the following error:
Could not resolve type id 'X' as a subtype of `MinimalXResult`: Class `FullXResult` not subtype of `MinimalXResult`
If this type of casting can be done without Jackson, that is fine too. Thanks for any help.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm trying to deserialize a byte array into a java type using jackson object mapper.
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class A {
String s;
String b;
}
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class B {
String c;
String b;
}
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class C {
List<CustomType> x;
}
and using the jackson method,
objectMapper.readValue(byte[] data, Class<T> type).
As i am not sure what object the byte array contains, i want to it to fail when it cannot create an object of the specified type. However, objectMapper returns an object with all fields initialized to null. How do i avoid this behavior?
Ex:
byte[] b; //contains Class B data
byte[] a; //contains Class A data
byte[] c// contains Class C data
A a = objectMapper.readValue(c, A.class) is returning
{s=null, b = null}
and this is ow i have configured the ObjectMapper,
#Bean
public ObjectMapper objectMapper(HandlerInstantiator handlerInstantiator) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.handlerInstantiator(handlerInstantiator);
builder.failOnEmptyBeans(false);
builder.failOnUnknownProperties(false);
builder.simpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
builder.timeZone("UTC");
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return builder.build();
}
#JsonIgnoreProperties(ignoreUnknown = true) says you ignore unknown properties. So if you unmarshal B data as A instance, the c property is ignored, just not filled so this is why you get null.
Remove this annotation an you should start getting JsonMappingException or similar.
#JsonInclude(JsonInclude.Include.NON_NULL) is just for serialization.
objectMapper returns an object with all fields initialized to null. How
do i avoid this behavior?
Fields in input object that match target class will not be set to null.
Therefore, make sure that there are some matching fields (fields with same name).
If you do not want null, you may have default values for those fields.
This may be done
at field declaration String s="default value";
in default, parameterless constructor. Jackson will invoke this constructor, then set values for matching fields from JSON data.
If your source json is totally different, not single matching field then you will get object where every field is null.
I'm using Jackson library in my Android project for parsing json responses.
In my case, the returned json array consists of objects of different structure each. I've created 3 VO classes, but I wonder if it is possible to tell jackson to try them on by one until any success.
The following could solve the problem, but it's forbidden, the same #JsonProperty but many possibilities:
#JsonProperty("Object")
public Class1 object1;
#JsonProperty("Object")
public Class2 object2;
#JsonProperty("Object")
public Class3 object3;
Any alternatives?
I would suggest to write a custom deserializer. You can find information on that here: http://www.baeldung.com/jackson-deserialization
Official docs are here: http://wiki.fasterxml.com/JacksonHowToCustomDeserializers
Alternative:
If the 3 objects are related to each other by implementing a common interface, you can do this:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
public interface MyObjectInterface {}
But then you will need to adjust the JSON to include the #class property. You did not state if that is possible or not.
Alternative 2:
If you already have a type property, you can do this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
#JsonSubTypes(
{#JsonSubTypes.Type(value = MyObject1.class, name = "OBJECT_1"),
#JsonSubTypes.Type(value = MyObject2.class, name = "OBJECT_2"),
#JsonSubTypes.Type(value = MyObject3.class, name = "OBJECT_3")}
)
public interface MyObjectInterface {}
the usual way to serialize to json and back is:
String catStr = mapper.writeValueAsString(cat);
Cat catOut = mapper.readValue(catStr, Cat.class);
Is there a way to add (maybe with annotation ) the type to the json on serialization and let the mapper take the value from it when it deserialize it?
so I can do the following
Object obj = mapper.readValue(catStr);
and later...
Cat catOut = (Cat)obj;
Thanks.
Sort of. You can add a property to the serialization which will indicate what class it is. And then when deserializing it, Jackson deduces the class automatically.
But you cannot deserialize it as Object, you need some base class/interface for all objects you want to behave like this. And that interface needs to use the #JsonTypeInfo annotation, signalling Jackson when deserializing this base class use the property class to distinguish which type.
Example:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
private abstract class Base {
}
private class A extends Base {
private final int i;
#JsonCreator
public A(#JsonProperty("i") int i) {
this.i = i;
}
}
When serialized will be:
{"#class":"com.test.A","i":3}
Testing code:
A a = new A(3);
String str = mapper.writeValueAsString(a);
Base base = mapper.readValue(str, Base.class);