Im attempting to convert a Java Object into JSON using the Jackson parser. The original object comes to me via protobuf and contains some #annotations specific to Jackson.
When I call String jsonView = objectMapper.writeValueAsString(values);
I get this exception. The object doesn't contain any recursive values so I'm not clear why Im seeing this error (or even this code path).
ObjectMapper is configured with: objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); (although removing it doesn't seem to change anything)
java.lang.NullPointerException: null
at com.fasterxml.jackson.databind.type.ResolvedRecursiveType.equals(ResolvedRecursiveType.java:103)
at com.fasterxml.jackson.databind.type.TypeBindings$AsKey.equals(TypeBindings.java:458)
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:940)
at com.fasterxml.jackson.databind.util.LRUMap.get(LRUMap.java:68)
at com.fasterxml.jackson.databind.type.TypeFactory._fromClass(TypeFactory.java:1211)
at com.fasterxml.jackson.databind.type.TypeFactory._fromParamType(TypeFactory.java:1384)
at com.fasterxml.jackson.databind.type.TypeFactory._fromAny(TypeFactory.java:1154)
at com.fasterxml.jackson.databind.type.TypeFactory._resolveSuperInterfaces(TypeFactory.java:1298)
at com.fasterxml.jackson.databind.type.TypeFactory._fromClass(TypeFactory.java:1243)
at com.fasterxml.jackson.databind.type.TypeFactory._fromAny(TypeFactory.java:1150)
at com.fasterxml.jackson.databind.type.TypeFactory._resolveSuperInterfaces(TypeFactory.java:1298)
at com.fasterxml.jackson.databind.type.TypeFactory._fromClass(TypeFactory.java:1247)
at com.fasterxml.jackson.databind.type.TypeFactory._fromAny(TypeFactory.java:1150)
at com.fasterxml.jackson.databind.type.TypeFactory.constructType(TypeFactory.java:618)
at com.fasterxml.jackson.databind.cfg.MapperConfig.constructType(MapperConfig.java:290)
at com.fasterxml.jackson.databind.cfg.MapperConfig.introspectClassAnnotations(MapperConfig.java:320)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.removeIgnorableTypes(BeanSerializerFactory.java:714)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:573)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:390)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:273)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:225)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:159)
at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1272)
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1243)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:535)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.resolve(BeanSerializerBase.java:332)
at com.fasterxml.jackson.databind.ser.SerializerCache.addAndResolveNonTypedSerializer(SerializerCache.java:174)
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1254)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:535)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.resolve(BeanSerializerBase.java:332)
at com.fasterxml.jackson.databind.ser.SerializerCache.addAndResolveNonTypedSerializer(SerializerCache.java:174)
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1254)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:535)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.resolve(BeanSerializerBase.java:332)
at com.fasterxml.jackson.databind.ser.SerializerCache.addAndResolveNonTypedSerializer(SerializerCache.java:197)
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1233)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:499)
at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:697)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:270)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3672)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3048)
Fixed in latest versions (see 2.8.1 or above):
https://github.com/FasterXML/jackson-databind/issues/1302
Related
I try to use the com.google.openrtb package https://github.com/google/openrtb in my micronaut project.
Google remove the support of JSON serialization in the last version of the package.
(If someone know how to convert the BidResponse of the last version of this package to a valid JSON, it will help a lot! )
So you can see my code :
Logger logger = Logger.getLogger(TestController.class.getName());
logger.info("ok");
BidResponse.Builder bid = BidResponse.newBuilder()
.setId("123")
.setCur("EUR")
.addSeatbid(BidResponse.SeatBid.newBuilder()
.addBid(BidResponse.SeatBid.Bid.newBuilder()
.setId("123")
.setAdid("ad-1234567")
.setImpid("123")
.setPrice(1.2))
);
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(bid);
return HttpResponse.status(HttpStatus.OK).body(json);
But when I call this function I get this error :
{"message":"Bad Request","_embedded":{"errors":[{"message":"Invalid JSON: No serializer found for class com.google.protobuf.UnknownFieldSet$Parser and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.google.openrtb.OpenRtb$BidResponse$Builder[\"seatbidList\"]->java.util.Collections$UnmodifiableRandomAccessList[0]->com.google.openrtb.OpenRtb$BidResponse$SeatBid[\"unknownFields\"]->com.google.protobuf.UnknownFieldSet[\"parserForType\"])"}]},"_links":{"self":{"href":"/test","templated":false}}}
I try to put SerializationFeature.FAIL_ON_EMPTY_BEANS configuration, And i get this error :
Invalid JSON: Direct self-reference leading to cycle (through reference chain: com.google.openrtb.OpenRtb$BidResponse[\"unknownFields\"]->com.google.protobuf.UnknownFieldSet[\"defaultInstanceForType\"])
Someone already got this type of error in Java ?
We have a Jackson ObjectReader that is overwriting any original data if it is missing from the json update request.
This is what the data structure looks like:
data class Model(
val fieldTypeA: FieldTypeA? = null,
)
data class FieldTypeA(
val valueA: String? = null,
val valueB: String? = null,
)
We then read an existing value from a database so that fieldTypeA.valueA = "Test"
val existingModel = repository.findById(id).getOrNull()
Then this line reads the existing data into an ObjectReader:
val readerForUpdating: ObjectReader = CustomMapper.readerForUpdating(existingEntity)
This is where the problem occurs. The readValue overwrites the fieldTypeA.valueA after this line is executed with a jsonRequest:
val updatedRequest: Model = readerForUpdating.readValue(jsonRequest)
jsonRequest:
{"fieldTypeA":{"valueB":"I am value B"}}
The existingEntity object now only contains fieldTypeA.valueB, with fieldTypeA.valueA getting overwritten with null.
Is there a way to tell Jackson not to overwrite when a value is missing from the JSON?
I've fixed my own problem in the following way, in case it helps someone in the future.
There was a new feature introduced in Jackson 2.9 which allows deep merging. To do this, the relevant property needs to be tagged with #JsonMerge.
So in my example question above, the Model object would be changed like this:
import com.fasterxml.jackson.annotation.JsonMerge
data class Model(
#JsonMerge
val fieldTypeA: FieldTypeA? = null,
)
This means if the original object contains a field with a value, it does not get overwritten to null when the new json comes in.
readerForUpdating(obj) only promises to use the root object you pass
Deserialization occurs normally except that the root-level value in JSON is not used for instantiating a new object; instead give updateable object is used as root
whereas fieldTypeA.valueA is an inner object.
It works on a shallow object:
val existingModel = FieldTypeA(valueA = "A")
println(existingModel)
val readerForUpdating: ObjectReader = MAPPER.readerForUpdating(existingModel)
val jsonRequest = "{\"valueB\":\"I am value B\"}"
val updatedRequest: FieldTypeA = readerForUpdating.readValue(jsonRequest)
println(updatedRequest)
produces
FieldTypeA(valueA=A, valueB=null)
FieldTypeA(valueA=A, valueB=I am value B)
It's hard to see how you could develop a feature like readerForUpdating() which would know which fields to adjust in a deep object graph. If the incoming object's object's field had a value, should that indicate an overwrite? but not if they come as nulls?
If I had to do this way myself, I might approach it this way:
read the incoming JSON into a traditional JSONObject then walk the keys and set the properties on the target object
For setting properties you could use old school Apache Commons BeanUtils tool, but I am not sure that that is maintained and it has had some vulnerabilities issues, so you could look at Spring's BeanWrapperImpl
It is out of desperation and I don't know java. Basically, I want to resolve a couchbase lite Document back to a react-native application as follows:
Document doc = database.getDocumet('id');
promise.resolve(doc);
that doesn't work with error can't be cast so I turned to toString(), hoping to parse it in react-native:
promise.resolve(doc.toString());
// in js:
const document = getNativeDocument('id');
console.log(document)
const v = JSON.parse(document);
but that fails too with error:
SyntaxError: JSON Parse error: Unexpected identifier "Document"].
The console result is:
Document{#0xb3181c18e30a7f1-1f61-49a0-9ca0-1dc3d2e343ed#1-a7ed608736c34cf5860a0d35968e6441e1f089ef(..):obj=>Dictionary{(..)a=>A,b=>B},age=>20,firstPromo=>null}
I have tried solutions to convert the Document into other times that can easily be resolved by promise.resolve() but now wondering if there is away to work with results of the .toString()
the toString method generates an internal representation of the object for debugging, not JSON of the document. You need to use a different method to serialize your Document to JSON.
The most popular option for this is to use the Jackson library: https://www.baeldung.com/jackson-object-mapper-tutorial
If you're using Spring framework with Spring data couchbase connector then Jackson's ObjectMapper should already be available to your application as a Bean.
I am taking a JSON file as input for a class and parsing the values using gson through respective data classes.
I want to call a function that takes a String value as an argument.
The string value allowed is decided from the values parsed from JSON file. Can I somehow check for that string value passed to the function at compile-time & give an error at compile-time?
Or If I can allow only certain values in the argument for the function based on the values from JSON
Detailed Explanation of use case:
I am building a SDK in which a the person using sdk inputs json String. The json is standardised and is parsed in my code.
{
"name": "Test",
"objects": [
{
"name": "object1",
"type": "object1"
}
]
}
Here name values and other values may vary based on the input by the developer using it but key remains same. But we need to call a function using the value in objects name parameter.
fun testMethod(objectName:String)
So developer calls the testMethod as testMethod(object1).
I need to validate object1 parameter based on json but is there any way possible restricting the test method parameter to object1 only & give error at compile time if the developer calls testMethod(obj1)
Right now I parse JSON & have checks inside the testMethod()
Sure it's possible to do, but somehow in different way, that you described. First of all, as you already mentioned this behavior could be done easily. For this purpose we have Objects.requireNotNull() or Guava.Preconditions(). At the same way you can define you checking but this will work on runtime only.
To do in compile time, you need to create Annotation Preprocessor. The same, as did in different libraries, and one of them, could be Lombok, with their NotNull and Nullable. Android annotation just provide mark and bound for IDE warning, but in their case they adding NotNull checking and throw exception for every annotation usage during compile time.
It's not an easy way, but it's what you are looking for.
No, it's impossible check it in compiler time. It's string handling, as numeric calculation.
In my app, I convert string to JSON and JSON to string, passing class descriptor. My aim is record JSON string in a text file to load in SQLite database. This code I've run in my desktop computer not in Android.
data class calcDescr (
...
)
val calc = CalcDescr(...)
// toJson: internal Kotlin data to JSON
val content = Gson().toJson(calc)
//==================
// Testing validity
// ================
// fromJson: JSON to internal Kotlin data.
// It needs pass the class descriptor. Uses *Java* token, but it's *Kotlin*
var testModel = Gson().fromJson(content, CalcDescr::class.java)
// toJson: internal Kotlin data to JSON again
var contentAgain = Gson().toJson(testModel)
// shoul be equal!
if (content == contentAgain) println("***ok***")
In my file, I write the variable content in a file
Long values in Objects generated by Cloud Endpoints are annotated with #JsonString. This causes a IllegalArgumentException when deserializing those Objects using a GsonFactory.
This is the stacktrace:
Caused by: java.lang.IllegalArgumentException: number type formatted as a JSON number cannot use #JsonString annotation [key updated, field private java.lang.Long com.google.api.services.timetable.model.Lesson.updated]
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:119)
at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:599)
at com.google.api.client.json.JsonParser.parse(JsonParser.java:350)
at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:586)
at com.google.api.client.json.JsonParser.parse(JsonParser.java:289)
at com.google.api.client.json.JsonParser.parse(JsonParser.java:266)
at com.google.api.client.json.JsonFactory.fromString(JsonFactory.java:207)
Example code to produce the Exception:
GsonFactory gsonFactory = new GsonFactory();
Lesson lesson = new Lesson();
lesson.setUpdated(2);
String json = gsonFactory.toString(lesson);
gsonFactory.fromString(json, Lesson.class);
Original discusssion https://groups.google.com/d/msg/endpoints-trusted-testers/-/_TKGoruZVt0J
The reason why this exception occurs is because the Java client library expects all long integers to be quoted (aka strings), because JavaScript can't handle 64-bit integer precision correctly. There's a known issue where the Python SDK won't correctly serialize 64-bit integers as strings. I'm not sure where you're getting the JSON from, exactly, but if it's in user code, you need to make sure you also have 64-bit integers quoted properly.