How do I get the Chinese text of Jackson's json generated data to be encoded in unicode

  • 2020-04-01 02:36:32
  • OfStack

As we all know, Jackson JSON is known for its speed, convenience, and flexibility. Previous articles described how to use annotations to specify how to serialize an object to JSON and how to deserialize a JSON data to an object. But one of the drawbacks is the treatment of Chinese. Of course the catch is that by default, Jackson JSON does not convert non-ascii characters such as Chinese characters to \uFFFF for display. That is to say by default will appear as a {" name ":" * * "} rather than {" name ":" \ u5F20 \ u4E09 "}. So why the need? In the HTTP protocol, we can specify the content encoding of the header part of the data. Such as: "GBK", "utf-8" and so on. If you set it correctly, then OK, the data represented by the former can be handled correctly. However, if the Settings are wrong, the Chinese characters will be garbled. If the two application systems are connected, the default codes used by both sides may be different. If one party changes the default codes, it will cause unpredictable consequences to the application. So if you can take a long view of development, then no matter what you set the encoding, the data will not generate garbled code. This is because the universal code, Unicode, is used.

Ok, so there's a problem. How do we solve it? By experimenting, Jackson JSON actually has Unicode encoded JSON data parsed by default. What is missing is the steps to serialize the object. Fortunately, the Jackson JSON framework allows us to customize our serialization methods. So let's write a serialization class:


import java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.impl.JsonWriteContext;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.util.CharTypes;
public class StringUnicodeSerializer extends JsonSerializer<String> {
 private final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
 private final int[] ESCAPE_CODES = CharTypes.get7BitOutputEscapes();
 private void writeUnicodeEscape(JsonGenerator gen, char c) throws IOException {
  gen.writeRaw('\');
  gen.writeRaw('u');
  gen.writeRaw(HEX_CHARS[(c >> 12) & 0xF]);
  gen.writeRaw(HEX_CHARS[(c >> 8) & 0xF]);
  gen.writeRaw(HEX_CHARS[(c >> 4) & 0xF]);
  gen.writeRaw(HEX_CHARS[c & 0xF]);
 }
 private void writeShortEscape(JsonGenerator gen, char c) throws IOException {
  gen.writeRaw('\');
  gen.writeRaw(c);
 }
 @Override
 public void serialize(String str, JsonGenerator gen,
   SerializerProvider provider) throws IOException,
   JsonProcessingException {
  int status = ((JsonWriteContext) gen.getOutputContext()).writeValue();
     switch (status) {
       case JsonWriteContext.STATUS_OK_AFTER_COLON:
         gen.writeRaw(':');
         break;
       case JsonWriteContext.STATUS_OK_AFTER_COMMA:
         gen.writeRaw(',');
         break;
       case JsonWriteContext.STATUS_EXPECT_NAME:
         throw new JsonGenerationException("Can not write string value here");
     }
     gen.writeRaw('"');//Writes the opening quote of the string in JSON
     for (char c : str.toCharArray()) {
       if (c >= 0x80){
        writeUnicodeEscape(gen, c); //Generates escaped unicode characters for all non-ascii characters
       }else {
         //Unicode characters escaped for the first 128 characters in ASCII characters
         int code = (c < ESCAPE_CODES.length ? ESCAPE_CODES[c] : 0);
         if (code == 0){
          gen.writeRaw(c); //There is no escape here
         }else if (code < 0){
          writeUnicodeEscape(gen, (char) (-code - 1)); //Universal escape character
         }else {
          writeShortEscape(gen, (char) code); //Short escape character (n t...)
         }
       }
     }
     gen.writeRaw('"');//Writes the end quote of the string in JSON
 }
}

This serialization class will use a method to handle string types for all places in the application that use Jackson JSON. You can't just have a method, you have to register it. Let Jackson JSON use the method just defined when serializing an object:

if (objectMapper== null){
 objectMapper= new ObjectMapper();
 //This field is ignored when the corresponding serializer cannot be found
 objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
 //Make Jackson JSON support Unicode encoded non-ascii characters
 CustomSerializerFactory serializerFactory= new CustomSerializerFactory();
 serializerFactory.addSpecificMapping(String.class, new StringUnicodeSerializer());
 objectMapper.setSerializerFactory(serializerFactory);
 //End of the support
}

Next, let's do a test object to verify our code:

import java.util.Date;
import net.csdn.blog.chaijunkun.util.DateDeserializer;
import net.csdn.blog.chaijunkun.util.DateSerializer;
import net.csdn.blog.chaijunkun.util.DateTimeDeserializer;
import net.csdn.blog.chaijunkun.util.DateTimeSerializer;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.codehaus.jackson.map.annotate.JsonSerialize;
@JsonPropertyOrder(alphabetic= false)
public class DemoObj {

 private Integer sid;

 private String stuName;

 private Boolean sex;

 @JsonSerialize(using= DateSerializer.class)
 @JsonDeserialize(using= DateDeserializer.class)
 private Date birthday;

 @JsonSerialize(using= DateTimeSerializer.class)
 @JsonDeserialize(using= DateTimeDeserializer.class)
 private Date logTime;
 //Getters and Setters

}

As you can see from the code, we do not enforce which sequence and antisequence methods to use for properties of type String. Then let's construct the test case:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import net.csdn.blog.chaijunkun.json.DemoObj;
import net.csdn.blog.chaijunkun.util.JSONUtil;
import org.apache.log4j.Logger;
public class JSONTest {

 private static Logger logger= Logger.getLogger(JSONTest.class);

 private static String json= "{"sid":2,"stuName":"u6C5Fu5357Style","sex":true,"birthday":"2012-07-15","logTime":"2012-12-04 19:22:36"}";

 public static void main(String[] args) {
  DemoObj objSrc= new DemoObj();
  objSrc.setSid(1);
  objSrc.setStuName(" Bird uncle ");
  objSrc.setSex(true);
  Calendar calendar= Calendar.getInstance();
  calendar.set(1977, Calendar.DECEMBER, 31, 0, 0, 0);
  objSrc.setBirthday(calendar.getTime());
  objSrc.setLogTime(new Date());
  logger.info(String.format(" convert JSON After the data :%s", JSONUtil.toJSON(objSrc)));
  DemoObj objDes= JSONUtil.fromJSON(json, DemoObj.class);
  if(objDes==null){
   logger.info(" Deserialization failure ");
  }else{
   logger.info(" Deserialization successful ");
   SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   logger.info(String.format(" Identification: %d", objDes.getSid()));
   logger.info(String.format(" Name: %s", objDes.getStuName()));
   logger.info(String.format(" Gender: %s", objDes.getSex()==true?" male ":" female "));
   logger.info(String.format(" Birthday: %s", sdf.format(objDes.getBirthday())));
   logger.info(String.format(" Login date: %s", sdf.format(objDes.getLogTime())));
  }
 }
}

Take a look at the output:

 convert JSON After the data :{"sid":1,"stuName":"u9E1Fu53D4","sex":true,"birthday":"1977-12-31","logTime":"2012-12-04 19:31:57"}
 Deserialization successful 
 Identification: 2
 Name: jiangnan Style
 Gender: male 
 Birthday: 2012-07-15 00:00:00
 Login date: 2012-12-04 19:22:36

We see that Chinese characters have been successfully displayed as unicode-encoded data. Similarly, the unicode-encoded data we constructed before was successfully displayed without any modification.

Careful friends may have observed that in the object definition code for the test, we specified different serialization and deserialization methods for the attributes "birthday" and "logTime" of the same type of Date. Let's see the difference between the two:


import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
public class DateTimeSerializer extends JsonSerializer<Date> {
 @Override
 public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
   throws IOException, JsonProcessingException {
  SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  String formattedDate= sdf.format(date);
  gen.writeString(formattedDate);
 }
}


import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
public class DateTimeDeserializer extends JsonDeserializer<Date> {
 @Override
 public Date deserialize(JsonParser parser, DeserializationContext context)
 throws IOException, JsonProcessingException {
  String dateFormat= "yyyy-MM-dd HH:mm:ss";
  SimpleDateFormat sdf= new SimpleDateFormat(dateFormat);
  try{
   String fieldData= parser.getText();
   return sdf.parse(fieldData);
  }catch (Exception e) {
   Calendar ca= Calendar.getInstance();
   ca.set(1970, Calendar.JANUARY, 1, 0, 0, 0);
   return ca.getTime();
  }
 }
}


import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
public class DateSerializer extends JsonSerializer<Date> {
 @Override
 public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
   throws IOException, JsonProcessingException {
  SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
  String formattedDate= sdf.format(date);
  gen.writeString(formattedDate);
 }
}


import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
public class DateDeserializer extends JsonDeserializer<Date> {

 @Override  public Date deserialize(JsonParser parser, DeserializationContext context)  throws IOException, JsonProcessingException {   String dateFormat= "yyyy-MM-dd";   SimpleDateFormat sdf= new SimpleDateFormat(dateFormat);   try{    String fieldData= parser.getText();    return sdf.parse(fieldData);   }catch (Exception e) {    Calendar ca= Calendar.getInstance();    ca.set(1970, Calendar.JANUARY, 1, 0, 0, 0);    return ca.getTime();   }  } }


From the code, we can see that the DateTimeSerializer and DateTimeDeserializer are more granular than the DateSerializer and DateDeserializer, adding time-specific attributes. This is very common in application development, we often know the date of birth information, and often need to be more detailed login time. From the example, we can know that even for the same type, we can get the desired data shape flexibly by formulating different sequence and anti-sequence methods. The above test cases are packaged. (link: http://xiazai.jb51.net/201312/yuanma/JSONUtil (jb51.net). Rar)

Supplement:

Recently, there was a requirement to modify the data when serializing and deserializing objects, and to change the generated JSON display to "visitor" when the data source value was found to be null. However, I did not specify the serializer or deserializer in any way. The program simply cannot get into the specified code. I later came to the conclusion that when Jackson JSON deserializes an object, if the corresponding attribute in the JSON data is null, it will not go through the custom deserializer. Likewise, when you set an object's property value to null, you don't go through the custom serializer when you serialize it to JSON. Therefore, if you have similar requirements, please judge and modify them in hard code before serialization and deserialization, and don't rely on serializers and deserializers for everything.


Related articles: