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

Can't serialize ByteArrayOutputStream value when using jackson 2.13.0 #3493

Closed
JavaRukawa opened this issue May 24, 2022 · 13 comments
Closed

Comments

@JavaRukawa
Copy link

JavaRukawa commented May 24, 2022

ByteArrayOutputStream bos = new ByteArrayOutputStream()
ObjectMapper om = objectMapper();
String json = om.writeValueAsString(bos);

public static ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}

when i write like this code, it goes wrong using jackson 2.13.0, but it works well using jackson 2.12.2

the error info:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.ByteArrayOutputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1300) at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:46) at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serializeWithType(UnknownSerializer.java:39) at com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4569) at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3822)
@JavaRukawa JavaRukawa added the to-evaluate Issue that has been received but not yet evaluated label May 24, 2022
@cowtowncoder
Copy link
Member

I am surprised that one could try to serialize ByteArrayOutputStream -- that is not supported usage and I don't remember ever adding support (or planning to).

What result would you expect? And what would be the use case?

@cowtowncoder cowtowncoder removed the to-evaluate Issue that has been received but not yet evaluated label May 24, 2022
@cowtowncoder cowtowncoder changed the title can't serialize ByteArrayOutputStream object when using jackson 2.13.0 Can't serialize ByteArrayOutputStream value when using jackson 2.13.0 May 24, 2022
@JavaRukawa
Copy link
Author

I am surprised that one could try to serialize ByteArrayOutputStream -- that is not supported usage and I don't remember ever adding support (or planning to).

What result would you expect? And what would be the use case?

ByteArrayOutputStream is used to serialize a photo object , it works well using jackson 2.12.2

@cowtowncoder
Copy link
Member

@JavaRukawa I don't understand. How could ByteArrayOutputStream be used that way? What would JSON look like? And how could it be read back?
I could see how one might expect ByteArrayInputStream (although would disagree due to transient nature of streams) to be useful for serialization. But not output stream.

@cowtowncoder
Copy link
Member

So. I do not see why or how ByteArrayOutputStream should be serializable, and Jackson does add any explicit support.
But if you simply want to serialize it as empty Object, you can configure ObjectMapper like so:

mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

to avoid exception and get {} as the serialization.

I will close this since I do not see anything to change at this point.

@ahgittin
Copy link

@cowtowncoder FYI this is a regression caused by #3117 where JDK classes could previously be serialized as beans using private fields

previously (confirmed with 2.11) with private visibility enabled it would serialize and deserialize fine, base64 encoding the BAOS.buf field as:

{"buf":"eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","count":1}

in 2.13 the visibility config override is ignored for java.* and javax.* per this code in MapperConfigBase:

    public final VisibilityChecker<?> getDefaultVisibilityChecker(Class<?> baseType,
            AnnotatedClass actualClass)
    {
        // 14-Apr-2021, tatu: [databind#3117] JDK types should be limited
        //    to "public-only" regardless of settings for other types
        VisibilityChecker<?> vc;

        if (ClassUtil.isJDKClass(baseType)) {
            vc = VisibilityChecker.Std.allPublicInstance();
        } else {
            vc = getDefaultVisibilityChecker();
        }

this means it is no longer possible to serialize JDK classes as beans based on private fields. given that java is becoming stricter about field access, and this was always possibly a bit dodgy, i don't have strong opinions on whether an opt-out for this is worth enabling.

the right thing for OP (and me) is probably custom serialization for BAOS based around BAOS.toByteArray().

@cowtowncoder
Copy link
Member

Thank you @ahgittin -- this makes sense. I will need to update release notes for 2.13 to say something about this.

I'll have to think about it a bit tho... I was about to say that this is completely unsupported behavior, given there is no API for accessing content. Except that, well, there is toByteArray().
But maybe it really ought to be supported given it "happened to work" earlier (and I never expected it'd have).

However: I do not think it should produce serialization it used to -- it should, in my opinion, produce simple Base64-encoded value.

I will file a new issue and can consider how to proceed.

@cowtowncoder
Copy link
Member

One more note: it is still possible to explicitly define Visibility levels via config overrides, I think so something like:

        mapper.configOverride(ByteArrayOutputStream.class)
            .setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.ALL,
                 JsonAutoDetect.Visibility.ANY));

would allow using the old behavior. Only the default settings have been changed wrt JDK types; it is still possible to override them.

@cowtowncoder
Copy link
Member

Filed #3522 for considering re-introducing serializability, but now with proper Binary value, not POJO.

@stolario
Copy link

It didn't help

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configOverride(BufferedInputStream.class).setVisibility(JsonAutoDetect.Value.construct(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY));
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter(objectMapper));

Error:

Type definition error: [simple type, class java.io.BufferedInputStream]
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class java.io.BufferedInputStream]
	at app//org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:489)
	at app//org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:103)
	at app//org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:1077)
	at app//org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:860)
	at app//org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:714)

@JooHyukKim
Copy link
Member

@stamanker As comments above and especially, #3493 (comment), this issue is closed and perhaps the new opened issue #3522 can help?

Also, just so you know, we need reports in plain Jackson to work with, meaning we need

  • ObjectMapper configuration : how you configured it
  • Classes to de/serialize
  • Expected input/output JSON and the actual, and
  • But "without" Spring, or other 3rd party frmworks/libs (they usually wrap Jackson with their own stuff)

and so on... whereas your provided stacktrace is in Spring at app//org.springframework.... which is difficult to be helped here.
Thanks!

@cowtowncoder
Copy link
Member

@stamanker You are talking about serializing BufferedInputStream -- which is not supported -- and this issue is about serializing ByteArrayOutputStream.

@stolario
Copy link

stolario commented Sep 20, 2023

@cowtowncoder it's the same issue - it was working before.
If anyone has same issue, here is what works:

        URL url = new File(filePath).toURI().toURL();
        MultiValueMap<String, Object> bodyMap = new LinkedMultiValueMap<>() {{
            add("file", new UrlResource(url) {
                @Override
                public String getFilename() {
                    return url.getFile();
                }
            });
        }};
        RequestEntity<?> request = RequestEntity
                .post(URI.create(URL))
                .headers(new HttpHeaders() {{
                    // auth header if required
                }})
                .body(bodyMap);

        ResponseEntity<YourDto> result = new RestTemplate().exchange(request, new ParameterizedTypeReference<>() {
        });

@cowtowncoder
Copy link
Member

@stamanker Ah. Yes, it is similar issue, but not same (from maintainer's POV) as it affects a different JDK class. Change itself may be due to similar underlying change (to prevent handling of JDK types as POJOs for security reasons) or might be something different.

It is possible to report this as breakage to fix if anyone cares about it: if so, that can be discussed in the new issue. From conceptual perspective this type differs so I'd probably not be inclined to support serializability -- even if it happened to be possible earlier (by virtue of default handling considering type just any old POJO).

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

5 participants