I'm trying to chase a bug where Jackson serializes some class ApiDeclaration { String apiVersion ... private JSONObject models } as {"apiVersion":"1.0.0", ..., "models"}" - note that "models" is a JSON key with the ':' and value missing!
This is in app where this worked before, and it's possible that a recent upgrade from Jackson 2.3.2 -> 2.7.4 may have broken this for us.
Does application code have to something particular for a "mixed bean" (i.e. a static Java class with some 'normal' fields but also a JSONObject) ?
Related
I use the openapi generator kotlin module to generate kotlin classes from my openapi.yaml file. The process works fine until I try to deserialize the received JSON in my code to a kotlin class using Jackson.
This is the generated class
data class Request (
#field:JsonProperty("name")
var name: kotlin.String,
)
This is the error I get
java.lang.IllegalArgumentException: Cannot construct instance of `...package.Request` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: UNKNOWN; byte offset: #UNKNOWN]
I noticed that when I remove the "#field:" part in the generated code, then everything works like a charm.
So now my question is can I either remove the #field from the generator or make Jackson deserialize it correctly?
The versions that I use are
jackson: 2.13.1
open-api-generator (gradle plugin): 5.3.0
I had the same error and registering the Kotlin Jackson module fixed it for me: https://github.com/FasterXML/jackson-module-kotlin
When I added an includeFilter to #EnableEntityDefinedRegion, it still scanned the whole entity package and created all Region beans. How do I scan the specific Region class? For example, only "Address" Region.
package org.test.entity
#Getter
#Setter
#Region("Address")
public class GfAddress implements Serializable
package org.test.entity
#Getter
#Setter
#Region("CreditCard")
public class GfCreditCard implements Serializable
package org.test.package
public interface IAddressRepository extends GemfireRepository<GfAddress, String>
package org.test.package
public interface ICreditCardRepository extends GemfireRepository<GfCreditCard , String>
#Service
#ClientCacheApplication
#EnableGemfireRepositories(basePackages = IAddressRepository.class, includeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes=AddressRepository.class))
#EnableEntityDefinedRegion(basePackages = GfAddress.class, includeFilters = #ComponentScan.Filter(type = FilterType.REGEX, pattern="GfAddress*"))
public class AddressDataAccess
When I print all the beans that are loaded, I found out that the following beans are created.
Address
CreditCard
IAddressRepository
AddressDataAccess
Version
GemFire : 9.8.6
spring-data-gemfire : 2.1.0
Spring Boot : 2.1.0
Sorry for the delay.
First, have a look at the SDG JIRA ticket I filed, DATAGEODE-352 - "EnableEntityDefinedRegions.includeFilters are inappropriately overridden by framework provided include filters".
In this ticket, I describe a couple of workarounds to this bug (!) in the comments, starting here.
I'd also be careful about your REGEX. I am no Regular Expression expert, but I am certain "GfAddress*" will not properly match the application entity type you are searching for and trying to match even when you pick up the new SDG bits resolving the issue I filed.
I created a similar test, using REGEX, to verify the resolution of the issue, here. This is the REGEX I specified. Using "Programmer*" did not work, as I suspected! That is because the REGEX is not valid and does not match the FQCN as used in the Spring RegexPatternTypeFilter.
Technically, it would be better to be a bit more specific about your type matching and use a "ASSIGNABLE_TYPE" TypeFilter instead, as this test demonstrates.
Finally, while SDG 2.1.x is compatible with GemFire 9.8.x, SD[G] Lovelace, or 2.1.x (e.g. 2.1.18.RELEASE), is officially based on, and only "supports", VMware GemFire 9.5.x (currently 9.5.4).
SDG 2.2.x is officially based on, and "supports", VMware GemFire 9.8.x (currently at 9.8.8).
You can review the new SDG Version Compatibility Matrix for more details.
If you have more questions, please follow up here or in DATAGEODE-352.
With Spring Data REST and Spring Data Mongo, I want to make a domain field (field username of domain User in my example) insertable when create but un-updatable when update. In other words an equivalent of JPA #Column(insertable = true, updatable = false).
I try a few approach but not work.
In my github project, domain class and repository are put in /src/main/java/*/*/User.java and UserRepository.java. The test is put in /src/test/java/*/*UserTest.java.
1. Spring Data annotation #ReadOnlyProperty and #Transient
The field is un-insertable when save to DB. See package readonlyproperty and transient_ in the project.
2. Jackson annotation #JsonProperty(access=READ_ONLY)
The field is un-insertable when create via POST request, because the JSON property is ignored when initiate an object. See package jsonpropertyreadonly in the project.
3. #JsonCreator on constructor and #JsonIgnore on setter
If the un-updatable field username is contained in json body of PUT or PATCH request, and username value changes, username get updated, which is unexpected. See package jsoncreator in the project.
4. Do not write a setter
same as 3. See package nosetter in the project.
5. Toggle on/off feature
spring.jackson.deserialization.fail-on-ignored-properties=false
spring.jackson.deserialization.fail-on-unknown-properties=false
spring.jackson.mapper.infer-property-mutators=false
not help
Spring Data REST PUT and PATCH Internal Implementation
PUT: it uses Jackson ObjectMapper.readerFor(Class) to initiate a new object
PATCH: it uses Jackson ObjectMapper.readerForUpdating(objectToUpdate).readValue(json), which use setter to update the objectToUpdate. Seems readerForUpdating doesn't see the #JsonIgnore on setter.
The only solution I know is implementing the setter in below way
void setUsername(String usernameToSet) {
if (null == this.username)
this.username = usernameToSet;
}
And disable PUT method, only use PATCH to update. See package setterchecknull.
Is there a better way? Thank you very much!!
When I try to serialize object containing Local date, I get following error:
csv generator does not support object values for properties
I have JSR-310 module enabled, with WRITE_DATES_AS_TIMESTAMPS and I can convert the same object to JSON without problem.
For now I resorted to mapping the object to another, string only object, but it's decadent and wasteful.
Is there a way for Jackson csv mapper to acknowledge localDates? Should I somehow enable JSR-310 specifically for csv mapper?
I had the same problem because of configuring mapper after schema. Make sure you are using the latest verson of jackson and its modules. This code works for me:
final CsvMapper mapper = new CsvMapper();
mapper.findAndRegisterModules();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); //Optional
final CsvSchema schema = mapper.schemaFor(PojoWithLocalDate.class);
// Use this mapper and schema as you need to: get readers, writers etc.
No additional annotations needed in Pojo class.
I want to retrieve a Map from a using JAX-RS (text/xml)
#GET
public Map<String,String> getMap(){
}
but I am getting the error below:
0000001e FlushResultHa E org.apache.wink.server.internal.handlers.FlushResultHandler handleResponse The system could not find a javax.ws.rs.ext.MessageBodyWriter or a DataSourceProvider class for the java.util.HashMap type and application/x-ms-application mediaType. Ensure that a javax.ws.rs.ext.MessageBodyWriter exists in the JAX-RS application for the type and media type specified.
[10:43:52:885 IST 07/02/12] 0000001e RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (500 - Internal Server Error) with message 'null' while processing GET request sent to http://localhost:9080/jaxrs_module/echo/upload/getSiteNames
The solution I choose is to wrap a Map and use it for the return param.
#XmlRootElement
public class JaxrsMapWrapper {
private Map<String,String> map;
public JaxrsMapWrapper(){
}
public void setMap(Map<String,String> map) {
this.map = map;
}
public Map<String,String> getMap() {
return map;
}
}
and the method signature will go like this
#GET
public JaxrsMapWrapper getMap()
Your problem is that the default serialization strategy (use JAXB) means that you can't serialize that map directly. There are two main ways to deal with this.
Write an XmlAdaptor
There are a number of questions on this on SO but the nicest explanation I've seen so far is on the CXF users mailing list from a few years ago. The one tricky bit (since you don't want an extra wrapper element) is that once you've got yourself a type adaptor, you've got to install it using a package-level annotation (on the right package, which might take some effort to figure out). Those are relatively exotic.
Write a custom MessageBodyWriter
It might well be easier to write your own code to do the serialization. To do this, you implement javax.ws.rs.ext.MessageBodyWriter and tag it with #Provider (assuming that you are using an engine that uses that to manage registration; not all do for complex reasons that don't matter too much here). This will let you produce exactly the document you want from any arbitrary type at a cost of more complexity when writing (but at least you won't be having complex JAXB problems). There are many ways to actually generate XML, with which ones to choose between depending on the data to be serialized
Note that if you were streaming the data out rather than assembling everything in memory, you'd have to implement this interface.
Using CXF 2.4.2, it supports returning Map from the api. I use jackson-jaxrs 1.9.6 for serialization.
#Path("participation")
#Consumes({"application/json"})
#Produces({"application/json"})
public interface SurveyParticipationApi {
#GET
#Path("appParameters")
Map<String,String> getAppParameters();
....
}
With CXF 2.7.x use
WebClient.postCollection(Object collection, Class<T> memberClass, Class<T> responseClass)
,like this in your rest client code.
(Map<String, Region>) client.postCollection(regionCodes, String.class,Map.class);
for other collections use WebClient.postAndGetCollection().