I've tried with the Swagger JaxRs current master 1.0, and the devel_2.0 branch for Swagger 2.0.
#ApiModel(value = "Animal",
subTypes = {Dog.class, Lion.class},
discriminator = "type")
public class Animal {
#ApiModelProperty(value = "the discriminator field.")
private String type;
And here is one of the sub classes,
#ApiModel(value = "Lion", parent = Animal.class)
public class Lion {
#ApiModelProperty(value = "the discriminator field.")
private String type;
I haven't found any many examples of what to expect, but here is the output in my current Swagger 2.0 projects swagger.json file.
"definitions":{
"Animal":{
"properties":{
"type":{
"type":"string",
"description":"the discriminator field."
}
},
"discriminator":"type"
},
No sign of the Dog or Lion object under definitions. Nothing in the request object. I'm not sure what this would look like if it worked, but let me know if you know how it should work.
All the code is here if you want to see the full context.
https://github.com/javatestcase/RestEasy/tree/RestEasyVersion2
Your examples helped me alot, so I thought I should help you in return because I got it working now!
You need to tell the serialisation/deserialisation how to bind the implementation:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME, // Were binding by providing a name
include = JsonTypeInfo.As.PROPERTY, // The name is provided in a property
property = "type", // Property name is type
visible = true // Retain the value of type after deserialisation
)
#JsonSubTypes({//Below, we define the names and the binding classes.
#JsonSubTypes.Type(value = Lion.class, name = "Lion"),
#JsonSubTypes.Type(value = Dog.class, name = "Dog")
})
#ApiModel(value = "Animal", subTypes = {Dog.class, Lion.class}, discriminator = "type")
public class Animal {
Related
I have a parent entity that holds a list of child entities. I'm using Spring Data Rest so there is no custom controller.
These are my entities:
#Entity
class Template(
childComponents: MutableList<AbstractLabelComponent> = mutableListOf(),
){
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
var id: Long? = null
#JsonManagedReference
#OneToMany(mappedBy = "template", cascade = [CascadeType.ALL], orphanRemoval = true)
private var components: MutableList<AbstractLabelComponent> = childComponents
set(value){
field.clear()
field.addAll(value)
field.forEach { it.template = this }
}
fun addComponent(component: AbstractLabelComponent){
component.template = this
this.components.add(component)
}
}
And the child class:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes(
JsonSubTypes.Type(value = TextComponent::class, name = TextComponent.TYPE),
JsonSubTypes.Type(value = TextAreaComponent::class, name = TextAreaComponent.TYPE),
JsonSubTypes.Type(value = ImageComponent::class, name = ImageComponent.TYPE)
)
abstract class AbstractLabelComponent(
#field:ManyToOne
#field:JsonBackReference
#field:JoinColumn(name="template_id")
open var template: Template?
){
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
open var id: Long? = null
}
This is the json I'm posting to the API:
{
"components": [
{
"id": 2300,
},
{
"id": 2302,
}
],
"id": 1789
}
I've left some simple fields out to make it more readable. I have no repository for components and the template repository has no code other than extending the basic crud repo.
At first, the child components were created without any problem but the template_id field was not set. This is why I created the setter for the components field to set the template. This works fine when I add components to a template that has no components, however I noticed that when I try to add more of them, they end up having no template ID again.
I've added a breakpoint to the setter and turns out, it is not called. I figured probably because if the list is not empty, JPA is not setting the list but instead adding elements to it. Am I right? If so, is there a way to tell it to use the addComponent method instead so I can set the template? Or any other way to ensure that a reference to the template is set?
Turns out, the difference between the two scenarios was not the number of components present in the database but whether there were any changes to the entity. Not sure but seems like JPA is not persisting the entity if there are no changes and probably it does not bother setting these kind of relationships either. There may be a better solution but the workaround is to set the template id in the JSON from the frontend if the entity is not new.
I am trying to serialize some JSON input to a data contract in Microsoft Dynamics 365 Finance. A simple data contract class works fine, but I cannot get data contract extensions to work. Does any one have experience with this or perhaps a working example?
The only related information I managed to find on this topic comes from this forum post.
Follow some hyperlinks and you will end up with the official Microsoft documentation (Ref# 199219), which states that this should be supported.
All variations of the data contract attributes below compile fine, but proved unsuccessful for me:
Using DataContract and DataMember instead of DataContractAttribute and DataMemberAttribute.
Combining DataContract and DataContractAttribute on a single method. (Produces runtime error about double serialization attribute.)
Repeating the DataContractAttribute on the extension class.
Additional experiments with the JSON deserializer class through its various constructor options also proved unsuccessful:
Passing a list of known types ClassA and ClassA_Extension.
Passing a list of known types ClassA_Extension and ClassA (in case the list order had an impact).
Passing a settings object and explicitly setting IgnoreExtensionDataObject to false (this appears to be the default).
Passing the extension class type as first parameter.
Update
A ticket was raised with Microsoft to investigate the issue. In their response they mentioned that they were able to reproduce this. They also declared that this was "by design" and "will not be fixed".
Our final solution will most likely be the following:
Build a mapping of DataMemberAttribute values and the corresponding data contract method.
Use a JavaScriptSerializer object to turn the JSON into a nested .NET dictionary object.
Iterate over the dictionary object and populate the data contract with the help of the mapping.
Example
Below is a miminal example to demonstrate my issue. The values of the variables value1 and value2 are populated as expected, but variable value3 remains empty.
Data contract
[DataContractAttribute('Class A')]
public class ClassA
{
protected str value1;
protected str value2;
[DataMemberAttribute('Value1')]
public str value1(str _value1 = value1)
{
value1 = _value1;
return value1;
}
[DataMemberAttribute('Value2')]
public str value2(str _value2 = value2)
{
value2 = _value2;
return value2;
}
}
Data contract extension
[ExtensionOf(classStr(ClassA))]
public final class ClassA_Extension
{
private str value3;
[DataMemberAttribute('Value3')]
public str value3(str _value3 = value3)
{
value3 = _value3;
return value3;
}
}
Serialization code with hard-coded input
public class ClassTest
{
public static void main(Args _args)
{
str inputJSON = #'{
"Value1": "abc",
"Value2": "def",
"Value3": "ghi"
}';
ClassA ret = new ClassA();
System.IO.MemoryStream ms = new System.IO.MemoryStream(System.Text.Encoding::UTF8.GetBytes(inputJSON));
System.Runtime.Serialization.Json.DataContractJsonSerializer dcjSer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(ret.GetType());
ret = dcjSer.ReadObject(ms);
ms.Close();
}
}
Result
It looks like the serializer is having issues. You might be able to pass a Type array similar to how FormRunConfigurationPropertyClassList does it?
I'm trying to get the "Name" of VCard properties, but I don't see a
"getName()" function. However, the following seems to work. Is it
legitimate to rely upon?
for (VCardProperty property : vCardThis.getProperties()){
String propKeyS = property.toString() ;
int atLoc = propKeyS.indexOf("#") ;
String propName = propKeyS.substring(0, atLoc) ;
System.out.println(propName) ;
}
The name that is assigned to the property when it is serialized is stored in the property's scribe class.
To get a property's scribe class, use the ScribeIndex class like so:
ScribeIndex index = new ScribeIndex();
for (VCardProperty property : vCardThis.getProperties()){
VCardPropertyScribe<? extends VCardProperty> scribe = index.getPropertyScribe(property);
System.out.println(scribe.getPropertyName());
}
I'm trying to deserialize two types of json:
{
name: "bob",
worksAt: {
name: "Bobs department store",
location: "downtown"
},
age: 46
}
and
{
name: "Tom",
worksAt: "company:Bobs department store",
age: 27
}
into these objects:
The first way creates two new objects, the second way requests the object from the database based on the contents of a string.
sort of like how jackson mapper can deserialize an arbitrary string into an object, for objects like this:
public class Company{
public String name;
public Employee[] employees
public Company(){}
public Company(String json){
//turn string into object using whatever encoding you want blah blah blah...
}
}
The trouble is I need both. I need it to handle objects and strings. Both could arrive from the same input.
The first think I tried was making a Converter
It says these create a delegate type to pass to the deserializer, but the converter is always applied even when the datatype isn't a string. So that didn't work.
I've also tried a normal deserializer, but I can't find a way to defer to the BeanDeserializer. The beanDeserializer is so complicated that I can't manually instantiate it. I also see no way to defer to a default deserializer in jackson mapper.
Do I have to re-implement jackson mappers deserialization to do this? Is there any way for a deserializer to say "I can't do this, use the default implementation."?
Edit: Some further progress. Based on the Jackson Mapper source code, it looks like you can instatiate bean deserializers like this:
DeserializationConfig config = ctxt.getConfig();
JavaType type = config.constructType(_valueClass);
BeanDescription introspect = config.introspect(type);
JsonDeserializer<Object> beanDeserializer = ctxt.getFactory().createBeanDeserializer(ctxt, type , introspect);
but for some reason all the _beanProperties have the FailingDeserializer set for their _valueDeserializer and the whole thing fails. So I have no idea why that happens...
Have you tried writing a custom deserializer? This gives you the most control on how Jackson deserializes the object. You may be able to try to deserialize one way, and if there's an error, try another way.
Jackson can also handle polymorphic deserialization, though this would require a small change to the json to include type information, and it sounds like your problem constraints might not allow that.
If I understand the problem correctly, I would recommend using JsonNode. You can define a setter in your top-level type like this:
setWorksAt(JsonNode node) {
if (node.getNodeType == JsonNodeType.STRING) {
String name = node.getText();
name = name.substring(name.lastIndexOf(':'));
this.company = new Company(name);
} else if (node.getNodeType == JsonNodeType.OBJECT) {
this.company = mapper.treeToValue(node, Company.class);
}
}
That allows you to handle the two separate worksFor inputs, while still allowing the standard mapper to handle any substructures for the OBJECT case.
With recent versions of Jackson (2.8+ I think, definitely works with 2.9) you can use multiple #JsonCreator and do something like this:
public class Company {
private String name;
private String location;
private Company(String name, String location) {
this.name = name;
this.location = location;
}
private Company(String stringRepresentation) {
// add code here to parse string and extract name and location
}
#JsonCreator
private static Company fromJson(
#JsonProperty("name") String name,
#JsonProperty("location") String location)
{
return new Company(name, location);
}
#JsonCreator
private static Company fromJson(String str) {
return Company(str);
}
}
Is there a way to specify #JsonTypeIdResolver on mapper config instead of annotating the target class ?
example, instead of :
#JsonTypeInfo(
use = JsonTypeInfo.Id.CUSTOM,
include = JsonTypeInfo.As.PROPERTY,
property = "#type")
#JsonTypeIdResolver(FooTypeIdResolver.class)
public class Foo {
}
Only have
#JsonTypeInfo(
use = JsonTypeInfo.Id.CUSTOM,
include = JsonTypeInfo.As.PROPERTY,
property = "#type")
public class Foo {
}
And do something like the following on the mapper
mapper.getSerializationConfig()
.with(new JacksonAnnotationIntrospector())
.without(SerializationFeature.INDENT_OUTPUT);
...
// something like this :
.addTypeIdResolver(Foo.class, FooTypeIdResolver.class)
I am asking this because the FooTypeIdResolver is on a different "library" which the Foo library cannot depend on...
You can use Jackson's mix-in annotations, in your case you would first create a class with the annotations you want to add:
#JsonTypeIdResolver(FooTypeIdResolver.class)
private static class FooMixIn {
}
And then you would apply this mix-in to the Foo class (roughly speaking this takes annotations present on FooMixIn and applies them to Foo)
mapper.getSerializationConfig().addMixInAnnotations(Foo.class, FooMixIn.class);
mapper.getDeserializationConfig().addMixInAnnotations(Foo.class, FooMixIn.class);
Handling of all annotations can be overridden by defining custom AnnotationIntrospector: usually by extending default JacksonAnnotationIntrospector. This can then use any alternate data source you want.
Mix-in annotations are the other obvious way to go, as explained in the accepted answer.