In the process of web development, data interaction is indispensable, which requires the relevant format of interactive data to be specified so that data can be passed between the client and the server. There are usually two types of data formats: 1. xml; 2. JSON. Generally speaking, JSON is used to pass data. This article introduces several problems encountered when converting JSON and objects in Java and related suggestions.
First, we have two concepts for JSON:
JSON object (JavaScript Object Notation, JavaScript object notation). This seems to be a custom JavaScript bit, but it is language- and platform-specific as a syntax. It just means that normally when we pass data to the server side (browser) we use JSON format, and this format is used to represent JavaScript objects. It consists of a series of "key-values", such as {"id": 1, "name": "kevin"}, which is somewhat similar to the way Map key-value pairs are stored. The JSON object described in Java actually refers to the JSONObject class, which is usually named after this name in each third-party JSONjar package. Different jar packages have slightly different internal implementations.
JSON string. The conversion between JSON objects and JSON strings is a process of serialization and deserialization, which is like the serialization and deserialization of Java objects. The data transmission in the network is performed through strings, or binary streams, etc. That is to say, when the client (browser) needs to pass data in JSON format, the string is passed on the network at this time, and the server side will of course also have a string (String type) after receiving the data. Sometimes it is necessary to convert the JSON string to a JSON object and then perform the next operation (String type is converted to JSONObject type).
The above two concepts basically clarify the data format of JSON, or also called JSON syntax. There are many jar packages for JSON in Java. The most "commonly used" is the jar package provided by "net.sf.json". This article will focus on this pit package. Although it is pit, it has a wide range of applications. In fact, there are other excellent JSON packages for us to use, such as fastjson, which Alibaba claims to be the fastest JSON package, Google's GSON, and Jackson. Try to use the "net.sf.json" package. Not only are there any pitfalls, but it is also very old, so old that it cannot download the source code in IDEA. The Maven repository shows that it was stopped in version 2.4 in 2010. Let’s talk about the two bugs I already know about “net.sf.json” and how these two bugs were generated.
JSON pit package in Java - net.sf.json
1. When Java objects convert JSON objects, all methods starting with get will be converted.
What does this mean, for example, the following Java objects are available.
package sfjson;import java.util.List;/** * Created by Kevin on 2017/12/1. */public class Student { private int id; private List<Long> courseIds; public int getId() { return id; } public void setId(int id) { this.id = id; } public List<Long> getCourseIds() { return courseIds; } public void setCourseIds(List<Long> courseIds) { this.courseIds = courseIds; } public String getSql() { //The method to get sql statements in this class does not have the corresponding attribute field return "this is sql."; }}When we convert Student object to JSON object, we hope that the converted JSON format should be:
{ "id": 1, "courseIds": [1, 2, 3] }However, the result after the conversion of the JSONObject json = JSONObject.fromObject(student); API is:
In other words, it can be guessed that "net.sf.json" gets the method that starts with the public modifier get in Java object, and defines its suffix as the "key" of the JSON object, and defines the return value of the method that starts with the get as the "value" of the corresponding key. Note that it is the method that starts with the public modifier get, and has a return value.
I think this is an unreasonable conversion rule. If I define a method in a Java object, and just because this method starts with "get" and has a return value, it will be exposed? Or is it exposed to the front-end Console console when it is returned to the client (browser)? The author stipulates this conversion rule. The rough reason I think is: since you define it as a public method and named it get, it is intentionally exposing this method so that the client calling it has the right to obtain it. But I still think this is unreasonable, and even I define it as a bug. It may not be reasonable for me to define this way, because according to my actual test, not only the "net.sf.json" package will be converted according to this rule, but fastjson and jackson also follow this rule, but Google's GSON does not convert objects to JSON according to this rule.
Convert the constructed Student object into a JSON object through JSONObject json = JSONObject.fromObject(student); and Student is as described above. After entering this method, the overloaded method fromObject (Object, JsonConfig) will continue to be called. In this overloaded method, instanceOf will be used to determine whether the Object object to be converted is enumeration, annotation and other types. These special types will have special judgment methods. Here is an ordinary Java POJO object, so it will enter _fromObject(Object, JsonConfig), and there will be some judgments in this method, and finally, the JSON object is created by calling defaultBeanProcessing. This method is the key, and it will continue to obtain the "property descriptor" through PropertyUtils.getPropertyDescriptors(bean) method. In fact, it is to obtain the method with get, which is encapsulated as a PropertyDescriptor here. This Student class will get 4, namely: getClass, getId, getCourseIds, getSql.
In fact, PropertyDescriptor is encapsulated in detail, and all read and write methods have been assigned.
For example, this getSql method has been parsed into the PropertyDescriptor in the figure above. The following is filtered out some methods through this class. For example, the getClass method is not a method in POJO, so it does not need to be converted into a JSON object. The propertyDescriptor is obtained through BeanInfo#getPropertyDescriptors, and the BeanInfo is obtained through new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo(); and then you will finally reach the following method.
private BeanInfo getBeanInfo() throws IntrospectionException { … MethodDescriptor mds[] = getTargetMethodInfo(); //This method will call getPublicDeclaredMethods. You can see that it is indeed looking for public methods, and all public methods, including wait and other PropertyDescriptors pds[] = getTargetPropertyInfo(); //Filter according to certain rules. The filtering rules are all in this method, which is to select the method where the public modifier has a get prefix and a return value...I briefly analyzed the source code of net.sf.json and found that as I guess, the specific source code is relatively large and limited in space, and needs to be viewed and tracked by myself.
2. When converting a Java object to a JSON object, a conversion error will occur on List<Long>
The title cannot be explained clearly in one sentence, and I am very sure that this problem is a bug.
Now there is a JSON string of {"id": 1, "courseIds": [1,2,3]}, and it needs to be converted to the Student object mentioned above. There are two attribute fields of type int and List<Long> in the Student object, which means that this JSON string should be converted to the corresponding data type.
String json = "{/"id/": 1, /"courseIds/": [1,2,3]}";Student student = (Student) JSONObject.toBean(JSONObject.fromObject(json), Student.class);System.out.println(student.getCourseIds().get(0) instanceof Long);The above output should be true, but unfortunately it is false. To be precise, it is Long at compile time, but Integer at run time. This has to be said to be a pitfall, and none of the other three JSON packages have such an error. So I'm sure it's a bug. Let’s see how this bug occurs in net.sf.json. You also need to compare the source code yourself to view it. While I was constantly deepening the breakpoint debug, I discovered that when net.sf.json was processing integer data, I discovered this method NumberUtils#createNumber. This class judges its data type when extracting data from a string. The original intention is to process the number as Long if it has "L" or "l" after it. From this point of view, the final result should be correct.
case 'L':case 'l': if (dec == null && exp == null && (numeric.charAt(0) == '-' && isDigits(numeric.substring(1)) || isDigits(numeric))) { try { return createLong(numeric); } catch (NumberFormatException var11) { return createBigInteger(numeric); } } else { throw new NumberFormatException(str + " is not a valid number."); }Indeed, so far, net.sf.json has accurately judged the data type through the identifier after the number. The problem lies in the fact that after obtaining this value and its data type, it needs to be stored in JSONObject. The method JSONUtils#transformNumber exists during the storage process. The existence of this method is at least purely extravagant in the current view.
public static Number transformNumber(Number input) { if (input instanceof Float) { return new Double(input.toString()); } else if (input instanceof Short) { return new Integer(input.intValue()); } else if (input instanceof Byte) { return new Integer(input.intValue()); } else { if (input instanceof Long) { Long max = new Long(2147483647L); if (input.longValue() <= max.longValue() && input.longValue() >= -2147483648L) { //Even if the original type is Long, as long as it is within the Integer range, it will eventually be converted to Integer. return new Integer(input.intValue()); } } return input; }}The above code clearly shows the culprit, whether it is the Long type (the Long type within the Integer range), including Byte and Short, it will be converted to Integer. I don't understand what the meaning of this code is. The accurate data type must be determined based on the letters after the number, and the accurate data type must be converted again later, which leads to the bug mentioned at the beginning. This problem is almost unavoidable, so the best way is not to use it.
These two pitfalls were discovered by chance. It is recommended not to use the JSON package of net.sf.json that has not been maintained. In addition, the net.sf.json package is not so strict in the verification of JSON format. If such a format is "{"id": 1, "courseIds": "[1,2,3]"}", an exception will be thrown in the other three packages, but net.sf.json will not.
The above article discusses the pitfalls of JSON and object transfer in Java in detail. This is all the content I share with you. I hope it can give you a reference and I hope you can support Wulin.com more.