- Basic Idea about Jackson’s classes and other important things:
- class ObjectMapper:
This mapper (or, data binder, or codec) provides functionality for converting between Java objects (instances of JDK provided core classes, beans), and matching JSON constructs. It will use instances of JsonParser and JsonGenerator for implementing actual reading/writing of JSON.
<e.g.> mapper.writeValueAsString(object)
mapper.readValue(json, Object.class) - class JsonGenerator and JsonParser:
JsonGenerator is responsible to write json text, while JsonParser is responsible to parse the 就json text. - class StdSerializer and StdDeserializer:
When JsonGenerator wants to generate json text, it needs to call all kinds of serializers to convert objects to corresponding json text. StdSerializer is the base class used by all standard serializers, and can also be used for custom serializers.
StdDeserializer plays similar role in deserializing process. - class JsonNode:
Jackson has a built-in tree model which can be used to represent a JSON object. Jackson's tree model is useful if you don't know how the JSON you will receive looks, or if you for some reason cannot (or just don't want to) create a class to represent it.
JSON tree model is represented by JsonNode. - Jackson Annotations:
The Jackson JSON toolkit contains a set of Java annotations which you can use to influence how JSON is read into objects, or what JSON is generated from the objects.
<e.g.,> @JsonProperty, @JsonCreator
- class ObjectMapper:
- Serialization: convert an object to a Json text:
———————————————————————————————————
ObjectMapper mapper = new ObjectMapper();
// write json to file: mapper.writeValue(File resultFile, Object value);
// generate a string: String json = mapper.writeValueAsString(value);
// write json to stream: mapper.writeValue(OutputStream out, Object value);
———————————————————————————————————
Let first take a look at a simplified process to serialize a standard java class.
For a user-defined class, how can JsonGenerator knows which field should be included in json text, and what should be its external property name (the key), there are four ways:- Use annotation @JsonProperty(“key_name”):@JsonPropery (also indicates that property is to be included) is used to indicate external property name, name used in data format (JSON or one of other supported data formats)
The annotation should be put in front of fields.e.g.,public class PersonValue {
@JsonProperty(“id”)
public long personId = 0;
@JsonProperty(“name”)
public String name = null;
}
//The Json text will be {“id”:0, “name”:null} - Use annotation @JsonGetter(“key_name”):
@JsonGetter is used to tell Jackson that a certain field value should be obtained from calling a getter method instead of via direct field access.
The annotation put in front of a method. The method’s return value is the value of json.e.g.,public class PersonValue {[Attention] @JsonProperty and @JsonGetter can be used together.
@JsonProperty(“id”)
public long personId = 0;
public String name = null;
@JsonGetter(“name”)
public String getName(){
return this.name;
}
}
//The Json text will be {“id”:0, “name”:null} - Use annotation @JsonValue:
The Jackson annotation @JsonValue tells Jackson that Jackson should not attempt to serialize the object itself, but rather call a method on the object which serializes the object to a JSON string.Deserialization: convert an Json text to an object:
e.g.,public class PersonValue {
public long personId = 0;
public String name = null;
@JsonValue
public String toJson(){
return “{\”id\”:” + this.personId + "," + “\”name\”:” +this.name +”}";
}
} - Self-define serializer
e.g.,@JsonSerialize(using = PersonValueSerializer.class)
public class PersonValue {
public long personId = 0;
public String name = null;
}
class PersonValueSerializer extends StdSerializer<PersonValue>{
public PersonValueSerializer(){
super(PersonValue.class, true);
}
@Override
public void serialize(PersonValue person, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonGenerationException {
//method-1: write person as an json array
jsonGenerator.writeStartArray();
jsonGenerator.writeNumber(this.personId);
jsonGenerator.writeString(this.name);
jsonGenerator.writeEndArray();
//method-2: write person as an json object jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField(“id”, this.personId);
jsonGenerator.writeStringField(“name”, this.name);
jsonGenerator.writeEndObject();
}
}
When ObjectMapper calls the function “writeValueAsString(person)”, it knows it should use the PersonValueSerializer to serialize the object.
If we use the method-1, the json text will be like “[271, “Hongkai Wu"]”.
If we use the method-2, the json text will be like “{“id”:271, “name”:”Hongkai Wu"}
- Use annotation @JsonProperty(“key_name”):@JsonPropery (also indicates that property is to be included) is used to indicate external property name, name used in data format (JSON or one of other supported data formats)
- Deserialization: convert a Json text to an object:
Similarly, let's take a look at simplified process of deserialization
There are three ways to construct a custom defined class' object from Json text- Use annotation @JsonCreator and @JsonProperty in a constructor
@JsonCreator: is used to help Jackson construct the Java object from Json text.
The annotation should be put in front of a class constructor.e.g.,public class PersonValue {
public long personId = 0;
public String name = null;
@JsonCreator
public PersonValue(@JsonProperty(“id”) id,
@JsonProperty(“name”) name){
this.id = id;
this.name = name;
}
}
When deserializing the json text, ObjectMapper knows it should call the constructor with the annotation @JsonCreator. It will map the @JsonProperty with the field name and read the field value. - Use annotation @JsonCreator and @JsonSetter(“key_name”)
@JsonSettoer is used to tell Jackson that is should match the name of this setter method to a property name in the JSON data, when reading JSON into objects.
The annotation should be put in front of a method
e.g.,public class PersonValue {
public long personId = 0;
public String name = null;
@JsonCreator
public PersonValue( //@JsonProperty(“id”) id,
@JsonProperty(“name”) name){
this.id = id;
this.name = name;
}
@JsonSetter(“id”)
public void setID(long id){
this.id = 0;
}
}
When deserializing the json text, ObjectMapper knows it should call the constructor and the function with the annotation @JsonSetter to construct the object. One thing worthy attention here is that when @JsonProperty(“id") in the constructor and @JsonSetter(“id") exist simultaneously, as shown in the above example, the ObjectMapper will ignore the @JsonSetter and use @JsonProperty to construct the field value. - Custom Defined Deserializer
For a custom defined deserializer, the deserialization process is shown in the graph below:
important API:
jsonParser.nextToken(): return the next JsonToken object
jsonParser.getCurrentName(): get the field name
jsonParser.readValueAs(ObjClass.class): read the value
jsonToken.equals(JsonToken.FIELD_NAME): return true if the token is a field
Let's read the following code example to understand the process:
e.g.,//@JsonSerialize(using = PersonValueSerializer.class)
@JsonDeserialize(using = PersonValueDeserializer.class)
public class PersonValue {
@JsonProperty(“id”) public long personId = 0;
@JsonProperty(“name”) public String name = null;
@JsonProperty(“friends”) public List<String> friends = new ArrayList<>();
}
class PersonValueDeserializer extends Deserializer<PersonValue>{
public PersonValueDeserializer(){
super(PersonValue.class);
}
@Override
public PersonValue deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
PersonValue p = new PersonValue();
//build an empty object, fill the fields later using JsonParser
while(!jsonParser.isClosed()){
JsonToken token = jsonParser.nextToken();
//JsonParser is like an iterator. The deserializing process is to read the tokens one by one.
if(token.equals(JsonToken.FIELD_NAME)){
String fieldName = jsonParser.getCurrentName();
//now we know the field name, next we need to know the field value
switch(fieldName){
case “id”:
jsonParser.nextToken(); //now we move the iterator to the field value
p.id = jsonParser.readValueAs(Long.class);
break;
case “name”:
jsonParser.nextToken();
p.name = jsonParser.readValueAs(String.class);
break;
case “friends”:
//the value of this field is an array, so the first token is JsonToken.START_ARRAY "["
jsonParser.nextToken();
while(jsonParser.nextToken() != JsonToken.END_ARRAY){
//the last token for the field “friends” should be JsonToken.END_ARRAY ]
p.friends.add(jsonParser.readValueAs(String.class);
}
//read all friends name until jsonParser’s token is END_ARRAY
break;
}
}
return p;
}
}
- Use annotation @JsonCreator and @JsonProperty in a constructor
- Other important annotations:
- @JsonInclude:
tells Jackson only to include properties under certain circumstances.
put in front of class defn.e.g.,@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class PersonValue {
…...
} - @JsonIgnore:
is used to tell Jackson to ignore a certain property (field) of a Java object.
put in front of fields.e.g.,public class PersonValue {
@JsonIgnore
public long personId = 0;
public String name = null;
}
- @JsonInclude:
- Jackson TreeModel
Difference between mutex vs spinlocks
I would start with a very basic technical interview question, that is : What is the difference between mutex and spinlock?
First of all, let’s understand what is a mutex?
Mutex is kind of a key to use any resources in the system. If you have mutex, you can use the resource, if you don’t wait till the mutex is released. The process goes to wait queue for that particular resource.
What is spin-lock? Spin lock is a mechanism in which the process needing a resource poll the lock on resource till it gets it. It is also called as busy-waiting. Process will be busy in a loop till it gets the resource.
So we get the first difference there itself, the way both wait for the resource lock. In case of mutex, process goes in wait state and does not use processor while in spin-lock, process keeps on looping and hence uses the processor even while waiting.
Second difference comes from : when should we use spin-lock or mutex?
Spin-locks are best used when a piece of code cannot go to sleep state. Best example of such code would be interrupts request handlers.
Mutexes are best used in user space program where sleeping of process does not affect the system in catastrophic way.
Spin locks make sense when we have multi-processor systems or we have uni-processor system with preemption enabled, spin locks used in uni-processor systems without preemption enabled, may lead to deadlock where process goes on spinning forever. Following table summarizes the above points.
Mutex Vs Spinlock
Criteria
|
Mutex
|
Spinlock
|
Mechanism
|
Test for lock.
If available, use the resource
If not, go to the wait queue
|
Test for lock.
If available, use the resource.
If not, loop again and test the lock till you get the lock
|
When to use
|
Used when putting process is not harmful like user space programs.
Use when there will be considerable time before the process gets the lock.
|
Used when the process should not be put to sleep like Interrupt service routines.
Use when lock will be granted in reasonably short time.
|
Drawbacks
|
Incur process context switch and scheduling cost.
|
Processor is busy doing nothing till lock is granted, wasting CPU cycles.
|
In next post we would list some the APIs which are used for using mutex and spinlocks in Linux kernel.
































