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

[BUG]fastjson 2.0.12, Enum序列化mixin不起作用 #703

Closed
leonchen83 opened this issue Aug 22, 2022 · 7 comments
Closed

[BUG]fastjson 2.0.12, Enum序列化mixin不起作用 #703

leonchen83 opened this issue Aug 22, 2022 · 7 comments
Labels
bug Something isn't working fixed
Milestone

Comments

@leonchen83
Copy link

问题描述

简要描述您碰到的问题。

环境信息

请填写以下信息:

  • OS信息: win10
  • JDK信息: jdk11
  • 版本信息:2.0.12

重现步骤

如何操作可以重现该问题:

import static com.alibaba.fastjson2.JSONWriter.Feature.WriteNonStringKeyAsString;
import static com.alibaba.fastjson2.JSONWriter.Feature.WriteNulls;

import java.io.IOException;
import java.lang.reflect.Type;

import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.reader.ObjectReaderProvider;
import com.alibaba.fastjson2.writer.ObjectWriter;
import com.alibaba.fastjson2.writer.ObjectWriterProvider;

/**
 * @author Baoyi Chen
 */
public class Main {
	public static void main(String[] args) throws IOException {
		Test t = new Test();
		t.setInt32(Misc.IntEnum.CLOSE);
		t.setInt64(Misc.LongEnum.HIGH);
		
		Main main = new Main();
		
		String s = main.write(t);
		System.out.println(s); // actual {"int32":1,"int64":0} expect {"int32":10,"int64":100}
		
		String json = "{\"int32\":10,\"int64\":100}";
		Test tt = main.read(json, Test.class);
		System.out.println(tt.int32);
		System.out.println(tt.int64);
	}
	
	public <T> T read(String json, Type type) {
		ObjectReaderProvider readers = new ObjectReaderProvider();
		readers.mixIn(Misc.PersistentEnum.class, PersistentEnumMixin.class);
		JSONReader.Context context = new JSONReader.Context(readers);
		try (final JSONReader reader = JSONReader.of(context, json)) {
			ObjectReader<T> v = reader.getObjectReader(type);
			return v.readObject(reader, 0);
		}
	}
	
	public String write(Object value) {
		ObjectWriterProvider writers = new ObjectWriterProvider();
		writers.mixIn(Misc.PersistentEnum.class, PersistentEnumMixin.class);
		JSONWriter.Context context = new JSONWriter.Context(writers);
		try (final JSONWriter writer = JSONWriter.of(context)) {
			if (value == null) writer.writeNull();
			else {
				context.config(WriteNonStringKeyAsString);
				context.config(WriteNulls);
				final Class<?> clazz = value.getClass();
				ObjectWriter<?> v = writer.getObjectWriter(clazz, clazz);
				v.write(writer, value, null, null, 0);
			}
			return writer.toString();
		}
	}
	
	public static class Test {
		Misc.IntEnum int32;
		Misc.LongEnum int64;
		
		public Misc.IntEnum getInt32() {
			return int32;
		}
		
		public void setInt32(Misc.IntEnum int32) {
			this.int32 = int32;
		}
		
		public Misc.LongEnum getInt64() {
			return int64;
		}
		
		public void setInt64(Misc.LongEnum int64) {
			this.int64 = int64;
		}
	}
	
	public interface PersistentEnumMixin<T> {
		@JSONField(value = true)
		T getValue();
	}
}
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;

/**
 * @author Baoyi Chen
 */
public class Misc {
	
	public static enum IntEnum implements PersistentEnum<Integer> {
		OPEN(6), CLOSE(10);
		
		int value;
		
		IntEnum(int value) {
			this.value = value;
		}
		
		@Override
		public Integer getValue() {
			return value;
		}
		
		@Override
		public Map<Integer, IntEnum> getAll() {
			Map<Integer, IntEnum> map = new HashMap<>();
			map.put(6, OPEN);
			map.put(10, CLOSE);
			return map;
		}
		
		public static IntEnum parse(int value) {
			if (value == OPEN.value) return OPEN;
			else if (value == CLOSE.value) return CLOSE;
			else throw new UnsupportedOperationException();
		}
	}
	
	public static enum LongEnum implements PersistentEnum<Long> {
		HIGH(100L), LOW(200L);
		
		long value;
		
		LongEnum(long value) {
			this.value = value;
		}
		
		@Override
		public Long getValue() {
			return value;
		}
		
		@Override
		public Map<Long, LongEnum> getAll() {
			Map<Long, LongEnum> map = new HashMap<>();
			map.put(100L, HIGH);
			map.put(200L, LOW);
			return map;
		}
		
		public static LongEnum parse(int value) {
			if (value == HIGH.value) return HIGH;
			else if (value == LOW.value) return LOW;
			else throw new UnsupportedOperationException();
		}
	}
	
	public interface PersistentEnum<T> {
		T getValue();
		
		Map<T, ? extends PersistentEnum<T>> getAll();
		
		static JavaLangAccess ACCESS = SharedSecrets.getJavaLangAccess();
		
		public static <T extends PersistentEnum<?>> Type getTypeArgument(Class<T> clazz) {
			//
			Type vs[] = clazz.getGenericInterfaces();
			if ((vs == null || vs.length == 0)) {
				vs = clazz.getEnclosingClass().getGenericInterfaces();
			}
			
			//
			ParameterizedType pt;
			for (int i = 0, n = vs.length; i < n; i++) {
				if (!(vs[i] instanceof ParameterizedType)) {
					continue;
				}
				pt = (ParameterizedType) vs[i];
				final Type rt = pt.getRawType();
				if (rt != PersistentEnum.class) continue;
				return pt.getActualTypeArguments()[0];
			}
			return null;
		}
		
		public static <T extends Enum<T> & PersistentEnum<?>> T parse(final Class<T> clazz, Object value) {
			if (value == null) return null;
			T[] constants = ACCESS.getEnumConstantsShared(clazz);
			T r = (constants == null || constants.length == 0) ? null : (T) ((PersistentEnum<?>) constants[0]).getAll().get(value);
			return r;
		}
	}
	
}

控制台输出

{"int32":1,"int64":0}
null
null

期待输出

{"int32":10,"int64":100}
CLOSE
HIGH
  1. 使用 xxx.xxx 方法
  2. 输入 ... 数据
  3. 出现 ... 错误
//可在此输入示例代码

期待的正确结果

对您期望发生的结果进行清晰简洁的描述。

相关日志输出

请复制并粘贴任何相关的日志输出。

附加信息

如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。

@leonchen83 leonchen83 added the bug Something isn't working label Aug 22, 2022
@leonchen83 leonchen83 changed the title [BUG] [BUG]fastjson 2.0.12, Enum序列化mixin不起作用 Aug 22, 2022
@leonchen83
Copy link
Author

并且使用mixin之后还有testcase有这种错误堆栈

Caused by: java.lang.UnsupportedOperationException
	at com.alibaba.fastjson2.writer.FieldWriter.getFieldValue(FieldWriter.java:80)
	at com.alibaba.fastjson2.writer.ObjectWriterAdapter.writeWithFilter(ObjectWriterAdapter.java:411)
	at com.alibaba.fastjson2.writer.ObjectWriterAdapter.write(ObjectWriterAdapter.java:274)
	at cn.nextop.gadget.json.fastjson.FastjsonMarshaller.write(FastjsonMarshaller.java:271)
	... 60 more

@wenshao wenshao added this to the 2.0.13 milestone Aug 22, 2022
@wenshao
Copy link
Member

wenshao commented Aug 22, 2022

@leonchen83
Copy link
Author

@wenshao
2.0.13-SNAPSHOT 在testcase中与main方法行为不一致

import static com.alibaba.fastjson2.JSONWriter.Feature.WriteNonStringKeyAsString;
import static com.alibaba.fastjson2.JSONWriter.Feature.WriteNulls;

import java.io.IOException;
import java.lang.reflect.Type;

import org.junit.jupiter.api.Test;

import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.reader.ObjectReaderProvider;
import com.alibaba.fastjson2.writer.ObjectWriter;
import com.alibaba.fastjson2.writer.ObjectWriterProvider;

/**
 * @author Baoyi Chen
 */
public class Main {
	public static void main(String[] args) throws IOException {
		Clazz t = new Clazz();
		t.setInt32(Misc.IntEnum.CLOSE);
		t.setInt64(Misc.LongEnum.HIGH);
		
		Main main = new Main();
		
		String s = main.write(t);
		System.out.println(s); // actual {"int32":1,"int64":0} expect {"int32":10,"int64":100}
		
		String json = "{\"int32\":10,\"int64\":100}";
		Clazz tt = main.read(json, Clazz.class);
		System.out.println(tt.int32);
		System.out.println(tt.int64);
	}
	
	@Test
	public void test() {
		Clazz t = new Clazz();
		t.setInt32(Misc.IntEnum.CLOSE);
		t.setInt64(Misc.LongEnum.HIGH);
		
		Main main = new Main();
		
		String s = main.write(t);
		System.out.println(s); // actual {"int32":1,"int64":0} expect {"int32":10,"int64":100}
		
		String json = "{\"int32\":10,\"int64\":100}";
		Clazz tt = main.read(json, Clazz.class);
		System.out.println(tt.int32);
		System.out.println(tt.int64);
	}
	
	public <T> T read(String json, Type type) {
		ObjectReaderProvider readers = new ObjectReaderProvider();
		readers.mixIn(Misc.PersistentEnum.class, PersistentEnumMixin.class);
		JSONReader.Context context = new JSONReader.Context(readers);
		try (final JSONReader reader = JSONReader.of(context, json)) {
			ObjectReader<T> v = reader.getObjectReader(type);
			return v.readObject(reader, 0);
		}
	}
	
	public String write(Object value) {
		ObjectWriterProvider writers = new ObjectWriterProvider();
		writers.mixIn(Misc.PersistentEnum.class, PersistentEnumMixin.class);
		JSONWriter.Context context = new JSONWriter.Context(writers);
		try (final JSONWriter writer = JSONWriter.of(context)) {
			if (value == null) writer.writeNull();
			else {
				context.config(WriteNonStringKeyAsString);
				context.config(WriteNulls);
				final Class<?> clazz = value.getClass();
				ObjectWriter<?> v = writer.getObjectWriter(clazz, clazz);
				v.write(writer, value, null, null, 0);
			}
			return writer.toString();
		}
	}
	
	public static class Clazz {
		Misc.IntEnum int32;
		Misc.LongEnum int64;
		
		public Misc.IntEnum getInt32() {
			return int32;
		}
		
		public void setInt32(Misc.IntEnum int32) {
			this.int32 = int32;
		}
		
		public Misc.LongEnum getInt64() {
			return int64;
		}
		
		public void setInt64(Misc.LongEnum int64) {
			this.int64 = int64;
		}
	}
	
	public interface PersistentEnumMixin<T> {
		@JSONField(value = true)
		T getValue();
	}
}
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;

/**
 * @author Baoyi Chen
 */
public class Misc {
	
	public static enum IntEnum implements PersistentEnum<Integer> {
		OPEN(6), CLOSE(10);
		
		int value;
		
		IntEnum(int value) {
			this.value = value;
		}
		
		@Override
		public Integer getValue() {
			return value;
		}
		
		@Override
		public Map<Integer, IntEnum> getAll() {
			Map<Integer, IntEnum> map = new HashMap<>();
			map.put(6, OPEN);
			map.put(10, CLOSE);
			return map;
		}
		
		public static IntEnum parse(int value) {
			if (value == OPEN.value) return OPEN;
			else if (value == CLOSE.value) return CLOSE;
			else throw new UnsupportedOperationException();
		}
	}
	
	public static enum LongEnum implements PersistentEnum<Long> {
		HIGH(100L), LOW(200L);
		
		long value;
		
		LongEnum(long value) {
			this.value = value;
		}
		
		@Override
		public Long getValue() {
			return value;
		}
		
		@Override
		public Map<Long, LongEnum> getAll() {
			Map<Long, LongEnum> map = new HashMap<>();
			map.put(100L, HIGH);
			map.put(200L, LOW);
			return map;
		}
		
		public static LongEnum parse(int value) {
			if (value == HIGH.value) return HIGH;
			else if (value == LOW.value) return LOW;
			else throw new UnsupportedOperationException();
		}
	}
	
	public interface PersistentEnum<T> {
		T getValue();
		
		Map<T, ? extends PersistentEnum<T>> getAll();
		
		static JavaLangAccess ACCESS = SharedSecrets.getJavaLangAccess();
		
		public static <T extends PersistentEnum<?>> Type getTypeArgument(Class<T> clazz) {
			//
			Type vs[] = clazz.getGenericInterfaces();
			if ((vs == null || vs.length == 0)) {
				vs = clazz.getEnclosingClass().getGenericInterfaces();
			}
			
			//
			ParameterizedType pt;
			for (int i = 0, n = vs.length; i < n; i++) {
				if (!(vs[i] instanceof ParameterizedType)) {
					continue;
				}
				pt = (ParameterizedType) vs[i];
				final Type rt = pt.getRawType();
				if (rt != PersistentEnum.class) continue;
				return pt.getActualTypeArguments()[0];
			}
			return null;
		}
		
		public static <T extends Enum<T> & PersistentEnum<?>> T parse(final Class<T> clazz, Object value) {
			if (value == null) return null;
			T[] constants = ACCESS.getEnumConstantsShared(clazz);
			T r = (constants == null || constants.length == 0) ? null : (T) ((PersistentEnum<?>) constants[0]).getAll().get(value);
			return r;
		}
	}
	
}

wenshao added a commit that referenced this issue Aug 24, 2022
@wenshao
Copy link
Member

wenshao commented Aug 24, 2022

@leonchen83
Copy link
Author

leonchen83 commented Aug 25, 2022

@wenshao

	@Test
	public void test() throws IOException {
		Main main = new Main();
		
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		System.setProperty("fastjson2.creator", "lambda");
		
		Clazz t = new Clazz();
		t.setInt32(Misc.IntEnum.CLOSE);
		t.setInt64(Misc.LongEnum.HIGH);
		
		main.write(new OutputStreamWriter(out), t);
		String s = new String(out.toByteArray());
		System.out.println(s); // actual {"int32":1,"int64":0} expect {"int32":10,"int64":100}
		
		String json = "{\"int32\":10,\"int64\":100}";
		Clazz tt = main.read(json, Clazz.class);
		System.out.println(tt.int32);
		System.out.println(tt.int64);
	}

当creator是lambda时,mixin不起作用

@wenshao
Copy link
Member

wenshao commented Aug 26, 2022

@leonchen83 问题已修复,请再更新快照帮忙验证下

@wenshao wenshao added the fixed label Aug 31, 2022
@wenshao
Copy link
Member

wenshao commented Sep 10, 2022

https://github.com/alibaba/fastjson2/releases/tag/2.0.13
2.0.13版本已经发布,请用帮忙用新版本验证

@wenshao wenshao closed this as completed Sep 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed
Projects
None yet
Development

No branches or pull requests

2 participants