-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(framework,actuator,common): replace fastjson with jackson #6701
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,73 +1,28 @@ | ||
| package org.tron.core.vm.trace; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonAutoDetect; | ||
| import com.fasterxml.jackson.core.JsonGenerator; | ||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import com.fasterxml.jackson.databind.JsonSerializer; | ||
| import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; | ||
| import com.fasterxml.jackson.annotation.PropertyAccessor; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.fasterxml.jackson.databind.SerializationFeature; | ||
| import com.fasterxml.jackson.databind.SerializerProvider; | ||
| import com.fasterxml.jackson.databind.introspect.VisibilityChecker; | ||
| import java.io.IOException; | ||
| import com.fasterxml.jackson.databind.json.JsonMapper; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.bouncycastle.util.encoders.Hex; | ||
| import org.tron.common.runtime.vm.DataWord; | ||
| import org.tron.core.vm.Op; | ||
|
|
||
| @Slf4j(topic = "VM") | ||
| public final class Serializers { | ||
|
|
||
| public static String serializeFieldsOnly(Object value, boolean pretty) { | ||
| try { | ||
| ObjectMapper mapper = createMapper(pretty); | ||
| mapper.setVisibilityChecker(fieldsOnlyVisibilityChecker(mapper)); | ||
| private static final ObjectMapper mapper = JsonMapper.builder() | ||
| .enable(SerializationFeature.INDENT_OUTPUT) | ||
| .visibility(PropertyAccessor.FIELD, Visibility.ANY) | ||
| .visibility(PropertyAccessor.GETTER, Visibility.NONE) | ||
| .visibility(PropertyAccessor.IS_GETTER, Visibility.NONE) | ||
| .build(); | ||
|
|
||
| public static String serializeFieldsOnly(Object value) { | ||
| try { | ||
| return mapper.writeValueAsString(value); | ||
| } catch (Exception e) { | ||
| logger.error("JSON serialization error: ", e); | ||
| return "{}"; | ||
| } | ||
| } | ||
|
|
||
| private static VisibilityChecker<?> fieldsOnlyVisibilityChecker(ObjectMapper mapper) { | ||
| return mapper.getSerializationConfig().getDefaultVisibilityChecker() | ||
| .withFieldVisibility(JsonAutoDetect.Visibility.ANY) | ||
| .withGetterVisibility(JsonAutoDetect.Visibility.NONE) | ||
| .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE); | ||
| } | ||
|
|
||
| public static ObjectMapper createMapper(boolean pretty) { | ||
| ObjectMapper mapper = new ObjectMapper(); | ||
| if (pretty) { | ||
| mapper.enable(SerializationFeature.INDENT_OUTPUT); | ||
| } | ||
| return mapper; | ||
| } | ||
|
|
||
| public static class DataWordSerializer extends JsonSerializer<DataWord> { | ||
|
|
||
| @Override | ||
| public void serialize(DataWord energy, JsonGenerator jgen, SerializerProvider provider) | ||
| throws IOException, JsonProcessingException { | ||
| jgen.writeString(energy.value().toString()); | ||
| } | ||
| } | ||
|
|
||
| public static class ByteArraySerializer extends JsonSerializer<byte[]> { | ||
|
|
||
| @Override | ||
| public void serialize(byte[] memory, JsonGenerator jgen, SerializerProvider provider) | ||
| throws IOException, JsonProcessingException { | ||
| jgen.writeString(Hex.toHexString(memory)); | ||
| } | ||
| } | ||
|
|
||
| public static class OpCodeSerializer extends JsonSerializer<Byte> { | ||
|
|
||
| @Override | ||
| public void serialize(Byte op, JsonGenerator jgen, SerializerProvider provider) | ||
| throws IOException, JsonProcessingException { | ||
| jgen.writeString(Op.getNameOf(op)); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| package org.tron.json; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||
| import com.fasterxml.jackson.core.json.JsonReadFeature; | ||
| import com.fasterxml.jackson.databind.DeserializationFeature; | ||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.MapperFeature; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.fasterxml.jackson.databind.SerializationFeature; | ||
| import com.fasterxml.jackson.databind.json.JsonMapper; | ||
| import com.fasterxml.jackson.databind.node.ObjectNode; | ||
|
|
||
| /** | ||
| * Drop-in replacement for {@code com.alibaba.fastjson.JSON}. | ||
| */ | ||
| public final class JSON { | ||
|
|
||
| public static final ObjectMapper MAPPER = JsonMapper.builder() | ||
| // Fastjson Feature.AllowUnQuotedFieldNames (default ON) | ||
| .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) | ||
| // Fastjson Feature.AllowSingleQuotes (default ON) | ||
| .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) | ||
| // Fastjson tolerates trailing commas (e.g. {"a":1,}) by default | ||
| .enable(JsonReadFeature.ALLOW_TRAILING_COMMA) | ||
| // Fastjson accepts NaN/Infinity as valid tokens | ||
| .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MUST] Please remove this flag from the shared request parser. This is reachable on current wallet HTTP paths, not just a theoretical Fastjson-parity issue. With this flag enabled, |
||
| // Fastjson accepts leading plus sign for numbers (e.g. +123) | ||
| .enable(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS) | ||
| // Fastjson accepts leading decimal point for numbers (e.g. .5) | ||
| .enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS) | ||
| // Fastjson accepts trailing decimal point for numbers (e.g. 5.) | ||
| .enable(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS) | ||
| // Fastjson accepts leading zeros for numbers (e.g. 007) | ||
| .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS) | ||
| // Fastjson accepts unescaped control chars in strings (e.g. raw tab/newline) | ||
| .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS) | ||
| // Fastjson accepts backslash-escaping any character (e.g. \q → q) | ||
| .enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER) | ||
| // Fastjson accepts Java-style comments (// and /* */) | ||
| .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | ||
| // Fastjson Feature.UseBigDecimal (default ON) | ||
| // https://github.com/alibaba/fastjson/wiki/deserialize_disable_bigdecimal_cn | ||
| .configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true) | ||
| // Fastjson Feature.IgnoreNotMatch (default ON) — unknown fields silently ignored | ||
| .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) | ||
| // Fastjson serializes empty beans as "{}" without error | ||
| .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) | ||
| // Fastjson omits null-valued fields by default (WriteMapNullValue is OFF by default) | ||
| // https://github.com/alibaba/fastjson/wiki/WriteNull_cn | ||
| .serializationInclusion(JsonInclude.Include.NON_NULL) | ||
| // Fastjson uses WriteDateUseDateFormat (string) not timestamps by default | ||
| .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) | ||
| // Fastjson smart-match: field names are matched ignoring case/underscores by default | ||
| // (DisableFieldSmartMatch is OFF by default → smart match ON) | ||
| .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) | ||
| .build(); | ||
|
|
||
| private JSON() { | ||
| } | ||
|
|
||
| /** | ||
| * Returns {@code true} when {@code text} is null, blank, or a | ||
| * case-insensitive {@code "null"} literal — mirroring Fastjson's lenient | ||
| * treatment of these inputs as JSON {@code null}. | ||
| */ | ||
| static boolean isNullLiteral(String text) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MUST] Preserve unquoted NULL compatibility: |
||
| if (text == null) { | ||
| return true; | ||
| } | ||
| String trimmed = text.trim(); | ||
| return trimmed.isEmpty() || "null".equalsIgnoreCase(trimmed); | ||
| } | ||
|
|
||
| public static JSONObject parseObject(String text) { | ||
| if (isNullLiteral(text)) { | ||
| return null; | ||
| } | ||
| try { | ||
| JsonNode node = MAPPER.readTree(text); | ||
| if (node == null || node.isNull()) { | ||
| return null; | ||
| } | ||
| if (!node.isObject()) { | ||
| throw new JSONException("can not cast to JSONObject."); | ||
| } | ||
| return new JSONObject((ObjectNode) node); | ||
| } catch (JSONException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| throw new JSONException(e.getMessage(), e); | ||
| } | ||
| } | ||
|
|
||
| public static <T> T parseObject(String text, Class<T> clazz) { | ||
| if (isNullLiteral(text)) { | ||
| return null; | ||
| } | ||
| if (clazz == JSONObject.class) { | ||
| return clazz.cast(parseObject(text)); | ||
| } | ||
| if (clazz == JSONArray.class) { | ||
| return clazz.cast(parseArray(text)); | ||
| } | ||
| try { | ||
| return MAPPER.readValue(text, clazz); | ||
| } catch (Exception e) { | ||
| throw new JSONException(e.getMessage(), e); | ||
| } | ||
| } | ||
|
|
||
| public static JsonNode parse(String text) { | ||
| if (isNullLiteral(text)) { | ||
| return null; | ||
| } | ||
| try { | ||
| JsonNode node = MAPPER.readTree(text); | ||
| if (node == null || node.isNull()) { | ||
| return null; | ||
| } | ||
| return node; | ||
| } catch (Exception e) { | ||
| throw new JSONException(e.getMessage(), e); | ||
| } | ||
| } | ||
|
|
||
| static JSONArray parseArray(String text) { | ||
| return JSONArray.parseArray(text); | ||
| } | ||
|
|
||
| public static String toJSONString(Object obj) { | ||
| return toJSONString(obj, false); | ||
| } | ||
|
|
||
| public static String toJSONString(Object obj, boolean pretty) { | ||
| if (obj == null) { | ||
| return "null"; | ||
| } | ||
| try { | ||
| if (obj instanceof JSONObject) { | ||
| return pretty ? MAPPER.writerWithDefaultPrettyPrinter() | ||
| .writeValueAsString(((JSONObject) obj).unwrap()) | ||
| : MAPPER.writeValueAsString(((JSONObject) obj).unwrap()); | ||
| } | ||
| if (obj instanceof JSONArray) { | ||
| return pretty ? MAPPER.writerWithDefaultPrettyPrinter() | ||
| .writeValueAsString(((JSONArray) obj).unwrap()) | ||
| : MAPPER.writeValueAsString(((JSONArray) obj).unwrap()); | ||
| } | ||
| return pretty ? MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj) | ||
| : MAPPER.writeValueAsString(obj); | ||
| } catch (Exception e) { | ||
| throw new JSONException(e.getMessage(), e); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[SHOULD] Avoid breaking
ProgramTrace.asJsonString(boolean)API: replacing it with a no-arg method introduces source/binary compatibility risk for external callers and removes compact-vs-pretty output control. Prefer keeping the original signature (or adding an overload) to preserve compatibility.