Skip to content
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

Non default value skipped in serialized json with serialization inclusion NON_DEFAULT #2105

Open
rpost opened this issue Aug 10, 2018 · 7 comments

Comments

@rpost
Copy link

rpost commented Aug 10, 2018

Running following code:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Test {

	@org.junit.Test
	public void name() throws Exception {
		ObjectMapper mapper = new ObjectMapper();
		mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);

		Pojo pojo = new Pojo();
		pojo.setValue(false);

		System.out.println(mapper.writeValueAsString(pojo));
	}

	public static class Pojo {
		private Boolean value;

		public Boolean getValue() {
			return value;
		}

		public void setValue(Boolean value) {
			this.value = value;
		}
	}
}

with Jackson v 2.8.2 outputs {"value":false} (as I expected). This however changes starting with 2.8.3: it outputs {} (tested with 2.8.3, 2.8.11.2 and 2.9.6). I guess it was affected by #1351. Was it intended? Imo that's bug: default value for Boolean is null, not false.

@workmanw
Copy link

workmanw commented Aug 24, 2018

This issue also burned us when upgrading from 2.6.6 to 2.9.6. It was extra confusing because of the breaking changes with NON_EMPTY and NON_DEFAULT going from 2.6.x to 2.7.x+.

This also changed with String. Previously "" would not be omitted with `NON_DEFAULT. After 2.8.3 it is omitted.

I'm reading a bunch of other tickets relating #1351 and I'm sympathetic because it's hard to know what the right answer is. But it would be great to know if this is a bug that's going to be fixed (and thus we might upgrade to 2.8.2) or if this is future functionality and we need to fix this in our application.

@cowtowncoder
Copy link
Member

At this point my goal is to try to make behavior match documentation, which I think states that meaning of NON_DEFAULT varies depending on where setting is found (as well as a bit on whether type is POJO or not).

When attached to a POJO Class (via class annotation, or JsonFormat set for class via config overrides), should exclude members with values they have for default instance of that POJO.

In other cases, including that of global default (serialization inclusion), NON_DEFAULT excludes values of default value of that type (which is straight-forwards for primitives and their matching wrappers; "" for String; "absent" for Reference Types (Optional et al), timestamp 0 for date/time values; null for other types).

The reason for such a complex setup is technical: figuring out default POJO property values can only be done when constructing POJO serializer (or at very least, that's what I concluded when trying to add new configuration levels).

For Jackson 3.0 I can try to unify things to try to make POJO-defaults work in all cases; and type-default only where not applicable (when referenced as non-POJO property, such as root value or element of an array, Collection or Map).

@workmanw
Copy link

@cowtowncoder Okay, I understand. That definitely makes it harder for us. I think it's worth amending the 2.8 and 2.8.3 release notes to list this as a breaking change to help give others a forewarning.

@bwaldvogel
Copy link

For Jackson 3.0 I can try to unify things to try to make POJO-defaults work in all cases; and type-default only where not applicable (when referenced as non-POJO property, such as root value or element of an array, Collection or Map).

I would appreciate to see a unification. The behavior in 2.8.3 and later is counter-intuitive in my opinion.

I’ve constructed a workaround to get the pre-2.8.3 behavior in 2.9.6: BeanSerializerFactoryWithGlobalIncludeDefaults.java
Note that this workaround could break in new Jackson releases.

Usage example:

 ObjectMapper mapper = new ObjectMapper();
 mapper.setSerializerFactory(new BeanSerializerFactoryWithGlobalIncludeDefaults());
 mapper.setSerializationInclusion(Include.NON_DEFAULT);

@itsavvy-ankur
Copy link

Is there anyone who has a work around to fall back to the behaviour prior to 2.8.3 ?
With Jackson 2.9.9 we are seeing similar issues where default values for String and Boolean are dropped from the response.

@Rolly992
Copy link

Rolly992 commented May 29, 2020

I've also encountered this quite contr-intuitive behaviour and may be the tests below can be useful to fix it or at least to be aware of it.
https://github.com/Rolly992/jackson-wrapper-types-non-default-unexpected/
From my point of view in Java default value for any object, including wrapper type, is null.
And Jackson is a java library, I think it should adhere to that.
Also, behavior is different based on if there are jsut public fields or there is a builder, getters and setters.

@cowtowncoder
Copy link
Member

@Rolly992 what you think is "default value" is not actually specification of what Jackson means by default PROPERTY value for deserialization purposes. Before sharing your personal interpretation of a term it makes sense to try to learn its meaning in context.

But to recap: idea of default value for, say

public class MyValue {
   public int x, y;

   public MyValue() {
        x = 3;
        y = 7;
   }

   // getters and setters

is that default value for x is 3, and y is 7 (in context of "property value of MyValue").
This because default instance of MyValue would have those values.
But implementing this can get tricky because to find real default values -- and not just what JDK initializes properties to, in absence of assignment -- one needs to have containing POJO type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants