Server Data Parser

Server Data Parser

_In the process of client development, one of the key content is to parse server data. Perhaps the first thing you will think about is which json to use to parse libraries.Yes, data transfer via json format is the mainstream at present. Indeed, different json parsing libraries have some differences in performance.Although the above issues are important, but often encountered in the development process is not performance-related issues, the author from the perspective of data use to share some experience.

  • The data member is set to private and the set method is not provided by default

    _Parsed data has a very low chance of being modified in the product, and data modification can easily lead to bug s.In addition, the process of data object parsing is often to construct objects directly through reflection. Based on the above two considerations, we advocate that data modification should follow the closing principle, that is, no set method is provided by default, and only a few cases allow data modification.

  • Provide a friendly data access interface

_Reading data often comes for the purpose of using specific scenarios. The most original data is usually not directly enough for use and often needs to be handled simply.Therefore, get methods provide a more friendly way to get data.The get method does not just return the most original data, but superimposes some simple processing or data assembly.
For example:

public class LearnInfo {

    // Raw data
    private int learnStatus;

    // Simple encapsulated method
    public boolean isLearned() {
        return learnStatus == 2;
    }
}

public class Example {

    private void doSomething1(LearnInfo learnInfo) {

        // Get raw data for business processing
        if (learnInfo.getLearnStatus() == 2) {
            // dosomething
        }
    }

    private void doSomething2(LearnInfo learnInfo) {

        // Get status information through a simple encapsulated interface
        if (learnInfo.isLearned()) {
            // do something
        }
    }
}
  • Isolate coupling between json strings and code calls

_Data parsing libraries often invoke member fields through reflection, resulting in a strict correspondence between the naming of member variables and the characters in the json string, which can be masked by the get method.
For example: Version 1.0

public class Course {

    private description;

    public String getDescription() {
        if (description == null) {
            return "";
        }
        return description;
    }
}

public class Example {
    private void doSomething(Course course) {
        mTextView.setText(course.getDescription());
    }
}

Version 1.1, back-end fields may change during version iteration for some reason

public class Course {

    // Modified here
    private desc;

    // Interface unchanged
    public String getDescription() {
        if (desc == null) {
            return "";
        }
        return desc;
    }
}

public class Example {

    // Caller does not need to modify
    private void doSomething(Course course) {
        mTextView.setText(course.getDescription());
    }
}
  • Debugging and Logging

_Android Studio allows breakpoint debugging for member variables through Field Watchpoint, and after introducing the get method, a general breakpoint debugging method is added.If necessary, you can also add logs to the get method.

  • Introducing data validity checking mechanism

_Once illegal data enters the system, it will bring a lot of exceptions. If the exception logic branch processing is insufficient at the time of code design, it can easily lead to system exceptions and even application crashes.Therefore, it is recommended to block the entry of anomalous data at the source as much as possible, and the earlier the processing, the smaller the impact on the later period.Generally speaking, it is appropriate to take data from the server as the first time to check the validity of the data when it is resolved into something truly meaningful in the system.Common illegal data

  1. A field that does not allow nulls returned null values

_The java language is not friendly with null pointers, and NullPointerException is one of the main reasons for the crash.Where this return value often occurs in the data structure during system iteration, a null value is returned.There are many solutions to this problem, one is to do system-wide exception capture, which is simple and rude, does not solve the problem from the root, is not elegant enough.Another option is to blank out all the places where data is used, which is cumbersome, easy to miss, and painful for developers.The author tries to solve this problem from the framework level in the process of project development.Support for Nullable and NotNull annotations is introduced to verify that field values conform to annotation descriptions during parsing by annotating when defining fields.Annotation is generally only appropriate for checks that do not allow null values in the back-end convention.In fact, to satisfy a better user experience, more fields are designed to allow null values.Therefore, in cases where null values are allowed to enter, the get method is designed to require that null values cannot be returned, providing a second level of protection.
For example:

public class Course {

    @NotNull
    private long id; // id does not allow null

    private List<Unit> units; // List can be empty
    private String description; // Description information may be empty

    // Direct return of null values is not allowed, reducing the cost of use for callers
    public List<Unit> getUnits() {
        if (units == null) {
            return new ArrayList();
        }
        return units;
    }

    // null values are not allowed to be returned, reducing the cost of use by the caller
    public String getDescription() {
        if (description == null) {
            return "";
        }
        return description;
    }
}
  1. The returned data does not conform to logic
    In some cases, the parsed data is not logical, for example:
public class PageInfo {
    private totalCount;
    private currentPage;
}
totalCount : 0, currentPage : 11

Due to improper exception handling on the server, the client resolved to total Count 0 and current Page 11, which is obviously not logical.Continuing to pass this data down is likely to cause an exception that crosses the bounds of the array, causing the application to crash.
Therefore, we also introduced a third level of protection, defining the LegalModel interface, with the following code:

public interface LegalModel {
    boolean check();
}

public class PageInfo implements LeagalModel {
    private totalCount;
    private currentPage;

    @Override
    public boolean check() {
        return totalCount > currentPage;
    }
}

public class Example {
    private void doSomething(PageInfo pageInfo) {
        if (!pageinfo.check()) {
            // Judging that the data is illegal
        }
    }
}
  • About Confusion

_When the json parsing library constructs a data object through reflection, the data object class cannot participate in confusion, otherwise the corresponding field name cannot be found.A common way to avoid confusion is to centralize data model classes under the same package name and configure paths in confusion configurations to avoid confusion.One obvious limitation of this approach is that when files change paths, they are appended to the configuration file.This restriction is very unfriendly, cumbersome, and particularly easy to miss during developer operations.Therefore, in this paper, the author recommends that you use an empty interface to solve the confusion problem.
The code is as follows:

// Define interfaces to prevent confusion
public interface NoProguard {

}

// Classes that do not require confusion to implement empty interfaces
public class Course implements NoProguard {

    private String description;

    // Internal classes also apply
    public static class LearnInfo implements NoProguard {
        private int learnStatus;    
    }
}

// Configuration Code
-keep interface com.example.NoProguard {*;}
-keep class * implements com.example.NoProguard {*;}
  • It is recommended that basic types be used instead of packaging classes
    The default value for wrapper classes is null, which can easily cause null pointer exceptions.The basic type has its own default value, which eliminates a lot of blank code.Take int and Integer for example. In some cases, the default value of 0 for int has some meaning. This scenario is not very likely. Generally, 0 can be avoided as much as possible in the convention.

Keywords: JSON Java

Added by macinslaw on Sun, 09 Jun 2019 19:36:36 +0300