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

Gson serialization from JsonSerializationContext triggers an exception if a typeadapter uses jsonValue #1289

Closed
Crystark opened this issue Apr 16, 2018 · 2 comments

Comments

@Crystark
Copy link

Hi,

I'm having trouble combining a JsonSerializer with a TypeAdapter that uses JsonWriter ::jsonValue

Here is the TypeAdapter for BigDecimals that I created:

	public class BigDecimalTypeAdapter extends TypeAdapter<BigDecimal> {
		public static final BigDecimalTypeAdapter INSTANCE = new BigDecimalTypeAdapter();

		@Override
		public void write(JsonWriter out, BigDecimal value) throws IOException {
			if (value == null) {
				out.nullValue();
			} else {
				out.jsonValue(value.stripTrailingZeros().toPlainString());
			}
		}

		@Override
		public BigDecimal read(JsonReader in) throws IOException {
			return TypeAdapters.BIG_DECIMAL.read(in);
		}
	}

And that is a generic serializer for one of my interfaces:

	public class MyInterfaceJsonSerializer implements JsonSerializer<IMyInterface> {
		@Override
		public JsonElement serialize(IMyInterface src, Type typeOfSrc, JsonSerializationContext context) {
			Map<String, Object> map = new TreeMap<>();
			
			Map<String, Object> map = new TreeMap<>();
			Map<String, Object> failed = new TreeMap<>();
			Method[] methods = IMyInterface.class.getMethods();

			for (int i = 0; i < methods.length; i++) {
				Method method = methods[i];
				String key = makePropertyName(method.getName());
				try {
					Object result = method.invoke(src);
					map.put(key, result);
				} catch (Exception e) {
					failed.put(key, e.getMessage());
				}
			}

			if (!failed.isEmpty())
				map.put("__failed", failed);

			return context.serialize(map); // Here that will call the BigDecimalTypeAdapter if any value is BigDecimal
		}
	}

When I try to serialize an instance of IMyInterface

java.lang.AssertionError
	at com.google.gson.internal.bind.JsonTreeWriter$1.write(JsonTreeWriter.java:36)
	at java.io.Writer.write(Writer.java:192)
	at java.io.Writer.write(Writer.java:157)
	at java.io.Writer.append(Writer.java:227)
	at com.google.gson.stream.JsonWriter.jsonValue(JsonWriter.java:436)
	at my.common.gson.BigDecimalTypeAdapter.write(BigDecimalTypeAdapter.java:22)
	at my.common.gson.BigDecimalTypeAdapter.write(BigDecimalTypeAdapter.java:1)
	at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:208)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:145)
	at com.google.gson.Gson.toJson(Gson.java:669)
	at com.google.gson.Gson.toJsonTree(Gson.java:562)
	at com.google.gson.Gson.toJsonTree(Gson.java:541)
	at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.serialize(TreeTypeAdapter.java:155)
	at my.service.ib.MyInterfaceJsonSerializer.serialize(MyInterfaceJsonSerializer.java:37)
	at my.service.ib.MyInterfaceJsonSerializer.serialize(MyInterfaceJsonSerializer.java:1)
	at com.google.gson.internal.bind.TreeTypeAdapter.write(TreeTypeAdapter.java:81)
	at com.google.gson.Gson.toJson(Gson.java:669)
	at com.google.gson.Gson.toJson(Gson.java:648)
	at com.google.gson.Gson.toJson(Gson.java:603)
	at com.google.gson.Gson.toJson(Gson.java:583)
	at my.service.ib.MyHandlerTest.test_gson(MyHandlerTest.java:26)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

This error comes from JsonTreeWriter.UNWRITABLE_WRITER::write.

I'm not sure if i'm doing something wrong here. If I change jsonValue to value it works nice except that I get double quotes around my BigDecimal values which i'd like to avoid.

Here is how i create my Gson object:

	public static final Gson				GSON	= new GsonBuilder()
		.registerTypeHierarchyAdapter(IMyInterface.class, new MyInterfaceJsonSerializer())
		.registerTypeAdapter(BigDecimal.class, BigDecimalTypeAdapter.INSTANCE)
		.create();

Am I missing something here ?

@lyubomyr-shaydariv
Copy link
Contributor

Not an issue. If you take a look at what the documentation says on jsonValue, you see that it's a low-level method not to be used in high-level manner:

Writes value directly to the writer without quoting or escaping.

This method is also not overridden in JsonTreeWriter (does it make any sense?) causing the AssertionError that's mostly ever an indicator that something is going totally wrong. Just replace the jsonValue method with the value method, and it will dispatch to the string overload that will properly escape and accumulate the the value into the internal JSON tree collected by JsonTreeWriter.

@JakeWharton
Copy link
Contributor

Thanks @lyubomyr-shaydariv!

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

Successfully merging a pull request may close this issue.

3 participants