When I attempt to deserialize:
{
"tags": {}
}
into:
public static class Foo
{
private final Set<URI> tags;
/**
* Creates a new Foo entity.
* <p/>
* #param tags the room tags
* #throws NullPointerException if tags is null
*/
#JsonCreator
public Foo(#JsonProperty("tags") Set<URI> tags)
{
Preconditions.checkNotNull(tags, "tags may not be null");
this.tags = ImmutableSet.copyOf(tags);
}
/**
* #return the tags
*/
public Set<URI> getTags()
{
return tags;
}
}
I get:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.HashSet out of START_OBJECT token
at [line: 1, column: 2]
Shouldn't this work out-of-the-box?
For this to work you need to convert to an array:
{
"tags": []
}
If not, how would you represent multiple elements in the tags set using valid JSON?
My problem was with mapping the payload, make sure that:
The fields are the same (payload and object) or you are using #JsonProperty correctly
You need to create setters on your object to framework fill the fields
I hope it helps
Related
I am doing deserialization at the Listener in Spring Kafka. But this assumes that the type information was included or sent by a Spring Kafka producer. In my case the Json is being sent across by the Debezium MySQLConnector and it does not add this meta data. So I would like to add it to the requests. I understand its placed in the request somewhere in the JsonSerializer, and I looked at the source code but could not figure out exactly how to use this to add meta data type during serialization generically to a request. In particular what field holds this type information? And is it the class name of the java object that was serialized? I dont think just setting a default serializer is going to work because I have multiple consumers listening on different topics as one would expect. Except for the simplest cases this is just not going to work to set one default as i have many consumers and types that I am deserializing to. So this answer is not going to work in my case Kafka - Deserializing the object in Consumer
Update tried using Method Types on deserializer but have another issue: Kafka Spring Deserialzer returnType static method never called
See
public abstract class AbstractJavaTypeMapper implements BeanClassLoaderAware {
/**
* Default header name for type information.
*/
public static final String DEFAULT_CLASSID_FIELD_NAME = "__TypeId__";
/**
* Default header name for container object contents type information.
*/
public static final String DEFAULT_CONTENT_CLASSID_FIELD_NAME = "__ContentTypeId__";
/**
* Default header name for map key type information.
*/
public static final String DEFAULT_KEY_CLASSID_FIELD_NAME = "__KeyTypeId__";
/**
* Default header name for key type information.
*/
public static final String KEY_DEFAULT_CLASSID_FIELD_NAME = "__Key_TypeId__";
/**
* Default header name for key container object contents type information.
*/
public static final String KEY_DEFAULT_CONTENT_CLASSID_FIELD_NAME = "__Key_ContentTypeId__";
/**
* Default header name for key map key type information.
*/
public static final String KEY_DEFAULT_KEY_CLASSID_FIELD_NAME = "__Key_KeyTypeId__";
2 sets of headers (keys and values).
TypeId is for simple classes
If TypeId is a container List<?>
ContentTypeId is the contained type.
If TypeId is a Map
Key_TypeId is the key type.
This allows you to reconstruct a Map<Foo, Bar>.
These headers can either contain fully qualified class names, or tokens that map to class names via the classIdMappings map.
However, since version 2.5, it would be easier to use the new
Using Methods to Determine Types.
That way, you can set your own headers and examine them in the method.
EDIT
Here is a simple example:
#SpringBootApplication
public class Gitter76Application {
public static void main(String[] args) {
SpringApplication.run(Gitter76Application.class, args);
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("gitter76").partitions(1).replicas(1).build();
}
#KafkaListener(id = "gitter76", topics = "gitter76")
public void listen(Foo in) {
System.out.println(in);
}
}
public class Foo {
private String bar;
public Foo() {
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.trusted.packages=com.example.demo
$ kafkacat -P -b localhost:9092 -t gitter76 -H __TypeId__=com.example.demo.Foo
{"bar":"baz"}
^C
2020-08-08 09:32:10.034 INFO 58146 --- [ gitter76-0-C-1] o.s.k.l.KafkaMessageListenerContainer : gitter76: partitions assigned: [gitter76-0]
Foo [bar=baz]
I have the following Spring Bean structure:
public abstract class XmlBaseChild {
protected Integer value;
protected String text;
#Autowired
transient protected MasterCodeService masterCodeService;
public XmlBaseChild(Integer value) {
setValue(value);
}
/**
* Set the Numeric value of the ChildView.
* This code is common for all childViews and handles a null value.
* #param value Numeric value of the ChildView
*/
#JsonProperty(value="id")
public void setValue(Integer value) {
if (value == null) {
this.value = null;
this.text = null;
return;
}
setConcreteValue(value);
}
/**
* Set the Numeric value of the ChildView.
* This code must be overridden by the concrete childViews.
* #param value Numeric value of the ChildView
*/
protected void setConcreteValue(Integer value){
boolean keyNotFound = true;
if (value != null && value > -1) {
this.value = value;
String messageKey = getValueFromMap(value, GetMasterCodeMapForChildView());
if (messageKey != null) {
this.text = LocalizeString(messageKey, null, getLocale);
keyNotFound = false;
}
}
if (keyNotFound){
throw new NotFoundException();
}
}
protected abstract Map<String, MasterCodeView> GetMasterCodeMapForChildView();
}
And the subclass:
#Component
#XmlRootElement(name=XmlDeployTool.VIEW_NAME)
public class XmlDeployTool extends XmlBaseChild {
public static Map<String, MasterCodeView> toolTypeCodes = new HashMap<String, MasterCodeView>();
/**
* Constructor for creating this object and preparing for marchalling (from java objects to xml/json).
* #param value Numeric value of the ChildView
* #param request HttpServletRequest
* #param includeSelf Include SELF link
* #param includeUP Include UP link
*/
public XmlDeployTool(Integer value) {
super(value);
}
/**
* Initialize the Tool Type codes after the component is wired (postconstruct),
* so that they are available in the constructor when an XmlDeploy object is created.
*/
#PostConstruct
protected void initializeDeployToolTypeCodes() {
toolTypeCodes = convertListToMap(masterCodeService.getToolTypeCodes());
}
#Override
protected Map<String, MasterCodeView> GetMasterCodeMapForChildView() {
return toolTypeCodes;
}
}
However, from what I understand from other posts like Order of #PostConstruct and inheritance, the #PostConstruct here normally executes AFTER the constructor is called. Then why is the toolTypeCodes map populated during the constructor? Is this part of the #Component annotation of Spring?
I also tried doing this with the masterCodeView map defined in the XmlBaseChild and only the PostConstruct method defined in the XmlDeployTool class, but that didn't work, the list didn't get initialized in that case. Why is this?
After checking the documentation and reading up some more, I figured out what's going on here:
Because my subclass is annotated with #Component, the PostConstruct triggers as part of the Spring startup process, even before any invocations of the normal constructor. Because of this, the static Map with MasterCodeViews gets populated, and since this is static, it stays populated as part of the subclass static properties. Because of this, this map has the proper usable data during construction.
When I tried to move the Map to the base class, In effect I turned this from a static property of the subclass to a static property of the subclass, which meant each constructor in turn populated it with the separate properties, leading to the map having the wrong data most of the time. When after that I tried to do this with a non-static map, the data wasn't retained when I invoked the constructor from code because this was effectively a new object with no initialized components.
I need to submit a JS-formed Array of Objects to a Spring MVC Controller. All the property names match.
#PostMapping("/addAdmin")
public void addAdmin(#RequestParam List<UserRolesGUIBean> userRolesGUIBeans)
{
// ...
}
JS:
var entries = [];
//...
// entries is an array of objects of the form {id: "..", role: ".."}
// verified to be correct before submission
$.ajax({
type : "post",
dataType : "json",
url : 'addAdmin',
data : JSON.stringify(entries)
})
Bean
public class UserRolesGUIBean implements Serializable {
private String id;
private String role;
// + Constructors (Empty + Full), Getters and setters
}
Error:
Required List parameter 'userRolesGUIBeans' is not present]
Also tried this with ModelAttribute and an ArrayList,
PostMapping("/addAdmin")
public void addAdmin(#ModelAttribute ArrayList<UserRolesGUIBean> userRolesGUIBeans) {
Now there are no errors, but the list is empty, no data was received.
Tried everything -- arrays vs. lists, JSON.stringify(data) or a data object with data {"entries" : entries}, RequestBody doesn't work and gives UTF Errors; and RequestParam as above doesn't work either.
This is way too complicated for a simple task.
You are trying to send a JSON object by using a post. You should use #RequestBody annotation.
Try to change your method in this way:
#PostMapping("/addAdmin")
public void addAdmin(#RequestBody List<UserRolesGUIBean> userRolesGUIBeans)
{
// ...
}
In this way Spring will intercept the Json and transform it in List of wished objects
SOLUTION:
1) In theory, if I was doing Form Submission (like $('#myForm').submit()), I could use #ModelAttribute to automatically bind my form to the bean. That's what #ModelAttribute does -- it's used for Form Submission. I don't have a real form; only my own custom values.
I could still "fake" a Form Submit by creating a Dynamic Form "on the fly," but I couldn't get the Arrayed-Field Form Submission (e.. obj[] with [] notation in the HTML Name) to map to a #ModelAttribute List<Bean>, so I disregarded this unusual Form Submit approach.
2) The real approach that worked is to just submit a custom JSON string which is my own. Not related to any form submission, so can't use #ModelAttribute. Instead, this is the #RequestBody approach. Then I have to parse the JSON RequestBody String myself -- and here we have to use Jackson, Java JSON, or GSON to parse the JSON Array.
In my case,
JS:
$.ajax({
type : "post",
dataType : 'json',
url : 'addAdmin',
data : JSON.stringify(entries)
})
Controller (note it takes a custom String only). Then it uses Jackson to parse the string manually, because Spring won't do it in this case. (Spring will only auto-parse if you're using #ModelAttribute form binding.)
#PostMapping("/addAdmin")
public boolean addAdmin(#RequestBody String json) throws Exception {
String decodedJson = java.net.URLDecoder.decode(json, "UTF-8");
ObjectMapper jacksonObjectMapper = new ObjectMapper(); // This is Jackson
List<UserRolesGUIBean> userRolesGUIBeans = jacksonObjectMapper.readValue(
decodedJson, new TypeReference<List<UserRolesGUIBean>>(){});
// Now I have my list of beans populated.
}
As promised, please go to https://start.spring.io/ and create a new project with a single depdendency for spring-boot-starter-web.
After that, you can create the following bean in your project.
import java.util.List;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class DemoController {
#PostMapping("/request-body")
public void getRequestBody(#RequestBody List<Person> list) {
for (Person person : list) {
System.out.println(person.name);
}
}
public static class Person {
private String name;
private String phoneNo;
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* #return the phoneNo
*/
public String getPhoneNo() {
return phoneNo;
}
/**
* #param phoneNo the phoneNo to set
*/
public void setPhoneNo(String phoneNo) {
this.phoneNo = phoneNo;
}
}
}
Nothing special, just take in a list of Person and print out the names. You can right click and run the project directly from the IDE.
You can open Postman and make a POST request as following.
This is what gets printed in the console.
If it works with Postman, you can make it work in JS. You just haven't figured out how. Instead of settling with that "workaround" you found, I think you should find out the proper way to submit a request in JS. In addition, some understanding of the Spring framework would help too. Otherwise, you will just keep randomly trying stuff like #ModelAttribute without getting anywhere.
How can I define a map with arbitrary keys in a Swagger model
Say I have the following internationalised model (in Ruby-style pseudocode, assuming use of something like Globalize)
class Thingy
translates :name
attribute :code
end
and my API wishes to be able to return something like
{
"thingy": {
"code": "barn",
"translations": {
"default": "barn",
"en": "barn",
"ru": "cарай",
"fr": "grange",
"nl": "schuur"
}
}
}
but I don't want to restrict the range of translation keys in the actual API
I can define in my swagger doc
definitions:
thingy:
required:
- code
properties:
code:
type: string
additionalProperties:
translations:
required:
- default
property:
default:
type: string
additonalProperties: string
That validates but the Swagger Codegen won't generate anything off the additionalProperties and it's not very explicit compared to somehow being able to define a map type with a mix of required and arbitrary keys.
Anyone working with internationalisation is going to face similar issues so my question is, how have other people dealt with this scenario?
This works under swagger-codegen-2.1.1-M1 (Java/JavaJaxRS) ... with Ron's suggestions ...
The YAML ...
translation:
required:
- default
properties:
default:
type: string
additionalProperties:
type: string
thingy:
required:
- code
properties:
code:
type: string
translations:
$ref: '#/definitions/translation'
creates a Map with a 'default' attribute ...
public class Translation extends HashMap<String, String> {
/**
*
*/
#Expose
private String _default = null;
/**
* #return _default the _default
*/
public String getDefault() {
return _default;
}
/**
* #param _default to set
*/
public void setDefault(String _default) {
this._default = _default;
}
}
Which in turn is embedded in a Thingy .....
public class Thingy {
/**
*
*/
#Expose
private String code = null;
/**
*
*/
#Expose
private Translation translations = null;
/**
* #return code the code
*/
public String getCode() {
return code;
}
/**
* #param code to set
*/
public void setCode(String code) {
this.code = code;
}
/**
* #return translations the Translations
*/
public Translation getTranslations() {
return translations;
}
/**
* #param translations the Translations to set
*/
public void setTranslations(Translation translations) {
this.translations = translations;
}
}
While the definition above is theoretically valid, it does not translate to what you're trying to describe nor is it really supported by Swagger.
In order to describe the structure you want, you'd need the following definition:
thingy:
type: object
required:
- code
properties:
code:
type: string
translations:
type: object
required:
- default
properties:
default:
type: string
additonalProperties:
type: string
While you can define the internal object inline as above, I'd highly recommend you externalize the definition and use $ref to reference it from the translations definition.
As for the code generator, support for maps has been introduced lately so it should work. If you find that it doesn't, please open an issue directly on the project containing a sample Swagger definition to help with debugging.
I am going through the Spring API. I went through the ModelAndView class. I found there two mehods in the class which return Map. One is getModel() and other is getModelInternal(). They both return Map. What is the difference between these methods.
Thank you.
Check javadoc for methods:
/**
* Return the model map. May return {#code null}.
* Called by DispatcherServlet for evaluation of the model.
*/
protected Map<String, Object> getModelInternal() {
return this.model;
}
/**
* Return the model map. Never returns {#code null}.
* To be called by application code for modifying the model.
*/
public Map<String, Object> getModel() {
return getModelMap();
}
So, one be called by client - another by framework, one nullable - another not-null.