yasson icon indicating copy to clipboard operation
yasson copied to clipboard

Custom De-Serialization Bug

Open garrettahines1 opened this issue 6 years ago • 7 comments
trafficstars

Yasson Version: 1.0.4 Java Version: 11.0.2 OS: Windows 10

When using a custom deserializer for a parameter of an object, any parameter object that comes after that custom deserialized object in the json string is not deserialized and is set to its default value (null, 0, etc.).

Example:

import java.util.List;

import javax.json.bind.annotation.JsonbTypeDeserializer;

public class A {

	private double a;
	@JsonbTypeDeserializer(BDeserializer.class)
	private List<B> b;
	private String c;

	public A() {

	}

	A(double a, List<B> b, String c) {
		this.a = a;
		this.b = b;
		this.c = c;
	}

	public double getA() {
		return a;
	}

	public void setA(final double a) {
		this.a = a;
	}

	public List<B> getB() {
		return b;
	}

	public void setB(final List<B> bObject) {
		this.b = bObject;
	}

	public String getC() {
		return c;
	}

	public void setC(final String c) {
		this.c = c;
	}
	
}
public class B {

	String bString;

	public B() {

	}

	public B(String b) {
		this.bString = b;
	}

	public String getbString() {
		return bString;
	}

	public void setbString(final String b) {
		this.bString = b;
	}
}
import java.lang.reflect.Type;
import java.util.List;
import java.util.stream.Collectors;

import javax.json.JsonValue;
import javax.json.bind.serializer.DeserializationContext;
import javax.json.bind.serializer.JsonbDeserializer;
import javax.json.stream.JsonParser;

public class BDeserializer implements JsonbDeserializer<List<B>> {
    @Override
    public List<B> deserialize(final JsonParser parser, final DeserializationContext ctx, final Type rtType) {
        final JsonValue arr = parser.getValue();

        return arr.asJsonArray().stream().map(i -> new B(i.toString())).collect(Collectors.toList());
    }
}
import java.util.List;

import javax.json.bind.Jsonb;
import javax.json.bind.spi.JsonbProvider;

import org.junit.jupiter.api.Test;

class Driver {

    private Jsonb jsonb = JsonbProvider.provider().create().build();

    @Test
    void test() {
        A a = new A(2.0, List.of(new B("test")), "test1");
        String json = jsonb.toJson(a);
        System.out.println("Serialized Result: " + json);
        A actual = jsonb.fromJson(json, A.class);
        System.out.println("Deserialized C Value: " + actual.getC());
    }
}

Output: Serialized Result: {"a":2.0,"b":[{"bString":"test"}],"c":"test1"} Deserialized C Value: null

Output after commenting out @JsonbTypeDeserializer(BDeserializer.class) in A class: Serialized Result: {"a":2.0,"b":[{"bString":"test"}],"c":"test1"} Deserialized C Value: test1

garrettahines1 avatar Oct 01 '19 14:10 garrettahines1

Checking in to see any update on this issue. This also happens in 1.0.5.

garrettahines1 avatar Oct 30 '19 12:10 garrettahines1

I have a similar issue with 1.0.5.

The bug is in JsonbRiParser: Some methods (getValue(), getArrayStream(), ...) don't level.pop();

use parser.getArray() directly and be happy :-).

Cheers,

D.

redmitry avatar Nov 06 '19 15:11 redmitry

hi @garrettahines1, thanks for raising this issue along with testcases!

Can you try @redmitry's suggestion of doing final JsonValue arr = parser.getArray(); instead of final JsonValue arr = parser.getValue(); in your custom deserializer and see if that works?

Also, did this ever work as intended prior to Yasson 1.0.4?

aguibert avatar Nov 06 '19 15:11 aguibert

@aguibert

Confirmed that doing parser.getArray() or parser.getObject() does work correctly in 1.0.5. 1.0.4 was the first version I used.

garrettahines1 avatar Nov 06 '19 15:11 garrettahines1

That's not about the List<> but about the JsonbRiParser wrapper. As I understand the problem, a custom deserializer calls parser.getValue() and consume events. Meanwhile, JsonbRiParser doesn't realize this and think we are still in the point of reading object. The effects could be different, but should lead to the consequent serialization fails. Note that the custom value is serialized correctly.

I did the small test (attached) to demonstrate the problem.

Cheers,

Dmitry yasson_test.zip

redmitry avatar Nov 06 '19 16:11 redmitry

I have faced the same issue with getValue. The javadoc says that:

If the parser state is START_ARRAY, the behavior is the same as getArray(). If the parser state is START_OBJECT, the behavior is the same as getObject(). For all other cases, if applicable, the JSON value is read and returned.

So I think it is clear that the getValue should act as the getObject and getArray in the implementation. I'm going to send a PR to master with a little test that also tests an Object. I have tested with Dmitry's test and also works with arrays.

rmartinc avatar Mar 11 '21 15:03 rmartinc

I think this is fixed in the current versions of yasson. Could you please close this as resolved?

rmartinc avatar Jul 30 '21 09:07 rmartinc