-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Disable the Number to String automatic conversion #796
Comments
What deserializer sees, literally, for: {
"numberAsString": 123
}```
is a sequence of events:
START_OBJECT, FIELD_NAME, VALUE_NUMBER_INT, END_OBJECT
|
Great! It is working. I now have my deserializer like this: public class ForceStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
if (jsonParser.getCurrentToken() == JsonToken.VALUE_NUMBER_INT) {
throw deserializationContext.wrongTokenException(jsonParser, JsonToken.VALUE_STRING, "Attempted to parse int to string but this is forbidden");
}
return jsonParser.getValueAsString();
}
} Like you were saying, a module is more convenient to have a global behavior, instead of annotate many fields. So I created a simple module, but it doesn't work in all cases. {
"numberAsString": 123
} This is fine but if I have nested objects, it doesn't go in the deserializer provided by the module. {
"myObject": {
"numberAsString": 123
}
} I guess it is because "myObject" is not a String. Is that correct? |
After some experiments, it is definitely because myObject is not a string. I created a module for MyObject type and I went to the associated deserializer. My second question is: |
No, that should not matter. Registered serializer is bound to type, not location, and should work for all fields declared with type One note on deserializer: perhaps check should rather verify that current token is of type |
Yes I want to allow false and true, actually I wanted to force only for the numbers. The thing is that when you have a number starting with 0, it is not working because of the leading 0. I wanted to make consistent and fail for every numbers, and force the user to use String instead. For the module, I got something like that: module.addDeserializer(String.class, new ForceStringDeserializer()) which is working fine for the first case. But not in the second case with the inner string. I tried: module.addDeserializer(String.class, new ForceStringDeserializer())
module.addDeserializer(MyObject.class, new ForceMyObjectDeserializer()) And I was getting in my second deserializer for the second case. But that's not very interesting as I will have to create as many deserializers as I have objects, in that case I prefer to duplicate the annotations... I am pretty sure, I am missing something, maybe you have an idea. |
@fabianpiau What I really need at this point are two things:
since behavior as you describe does not make sense. Globally registered deserializer should be applied both for "root value" and transitive property values. |
The first one is pretty simple. A POJO MyObject with a String field. |
Unfortunately I can not reproduce the issue. I can run test: static class UCStringDeserializer extends StdDeserializer<String> {
public UCStringDeserializer() { super(String.class); }
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
return p.getText().toUpperCase();
}
}
protected static class StringWrapper {
public String str;
public StringWrapper() { }
public StringWrapper(String value) {
str = value;
}
}
public void testCustomStringDeser() throws Exception
{
ObjectMapper mapper = new ObjectMapper().registerModule(
new SimpleModule().addDeserializer(String.class, new UCStringDeserializer())
);
assertEquals("FOO", mapper.readValue(quote("foo"), String.class));
StringWrapper sw = mapper.readValue("{\"str\":\"foo\"}", StringWrapper.class);
assertNotNull(sw);
assertEquals("FOO", sw.str);
} This is with 2.6.0-rc1, but should work on any 2.x version. So there is something special about |
Ok, I know why it is working in the case there are no inner properties. Actually, you're right, there is no relation with the fact there are inner properties. I was starting to do some copy/paste then I saw a difference between the working case and the not working one. public class MyObjectWorking {
private String numberAsString;
@JsonCreator
public MyObjectWorking (String numberAsString) {
this.numberAsString = numberAsString;
}
// Getter and setter
} The constructor with the JsonCreator annotation is the difference. I removed it, and I was not going in the custom deserializer anymore. In my other object (more complex with a nested object), I don't have a constructor. Do you know how to make it work without having to create a constructor? I try to add a constructor for my second object, but then I have other issue. ! java.lang.IllegalArgumentException: Argument #0 of constructor. |
I think at this point full example would make most sense, since I get confused with kinds of cases that work, and with what JSON. However, I suspect the difference here is between two constructors: @JsonCreator // explicitly delegating, calls registered String deserializer, then passes String
public POJO(String str) { ... }
// Implicit constructor, String passed as-is directly, no deserializer
public POJO2(String str) { } As to whether second case should go through deserializer or not.... I am not sure. |
I remove the @JsonCreator annotation on the constructor (of the working example). And it still works. I am not sure whether this annotation is really used or not, I prefer to keep it in case of regression. At the end, I will keep the duplicated annotations on a couple of fields, I don't want to spend too much time on this. Thanks for your help :) |
@fabianpiau Makes sense to me. |
@fabianpiau |
thanks for this discussion and provided "workaround". But this should really be DeserializationFeature. Automatic type conversion are evil and as such belongs to javascript. It should have never been on turned on by default, and thus there should be easy way to turn it off at least. It's kinda weird have to write ForceStringDeserializer into every project. Can someone reconsider adding it? |
I also have this issue. This should be a feature, something like I also have other issues, for example, if the field is a boolean, What's the best way of doing this? EDIT: http://fasterxml.github.io/jackson-databind/javadoc/2.9/com/fasterxml/jackson/databind/MapperFeature.html#ALLOW_COERCION_OF_SCALARS seems to do the trick. |
@martin-mucha If I want to hear a rant on your personal opinion on things, I'll be sure to ask. Until then, please try to stick to facts, and write blog posts somewhere else. All of coercions have been added by user requests and contributions, so while I definitely agree that they should and need to be configurable (and well documented), there is strong demand for more permissive acceptance of values. This is especially because your client may well be written in Javascript or Perl or one of many, many languages (and libraries, frameworks), with various limitations on kinds of things they can or will produce. |
From this, most I am going to say is that I hope to have time to refine this: the problem is that adding tons of |
@cowtowncoder Please take it easy. There's no point in arguing about one sentence, I wrote it, sorry, next time I will be careful. I just asked to reconsider to have this possibility readily available for everyone. I have this 'turning off of unwanted features' in all my projects already, this might just be useful for others as well. Java is typically more strict language and users do want to have strong validations. Some environments do not tolerate lenient behavior. |
I have similar constraints. Something like |
I would also be interested in this feature. |
#2113 to be released in 2.12.0 should allow strict handling of many of use cases. |
Hi, I've upgraded to 2.12.1 and I was able to use I have a similar setting as below
The unit test below failed
Has the case above been supported yet? Thanks! |
@ZengruiWang there is a new open issue, #3013 that covers this I think. Short answer is that your usage is correct but existing deserializers do not yet check for this particular coercion. They should. |
By default, Jackson is converting numbers to strings. In most cases, that's fine. But not in my situation unfortunately. Is there a way to prevent that?
For example:
JSON
JAVA
It works just fine. But the thing is that I want this automatic boxing not enabled and got an exception instead. So the user is forced to use a String. I.e.
I don't think such a feature exist, I look on http://wiki.fasterxml.com/JacksonFeaturesGenerator and a bit everywhere like http://stackoverflow.com/questions/7806316/jackson-json-converts-integers-into-strings
I tried to create a custom deserializer, but it seems that the value is already converted as a string when I got to the deserializer, and I don't have the possibility to determine if it was a string or an integer originally in the request.
Thanks
Fabian
The text was updated successfully, but these errors were encountered: