Solve the Json/Java polymorphism request by Jackson

By W.ZH

When convert between json and jave objects, you could often face the polymorphism issue.
Such as a json of Content could be:

{
  "type":"valuea",
  "value": {
    "valueaName": "corn",
    "bar":  "sweet"
  }
}

and also could be

{
  "type":"valueb",
  "value": {
    "valuebName": "toy",
    "color":  "yellow",
    "price":  "20"     
  }
}

Value objects in fact is depends on the type data to change. This requires that depending on the type data and create different value’s object at Java side, How to do?

Here is the solution at the Jackson code by using the     @JsonTypeInfo

let us define a abstract class Value and another two class ValueA and ValueB to Extends it.

public abstract class Value {
}

and ValueA to inherit Value

@JsonInclude(JsonInclude.Include.NON_NULL)
public class ValueA extends Value {

    @JsonProperty("valueaName")
    private String valueaName = "";

    @JsonProperty("bar")
    private String bar;
....................................
....................................
}

and ValueB Class to inherit Value.

@JsonInclude(JsonInclude.Include.NON_NULL)
public class ValueB extends Value {

    @JsonProperty("valuebName")
    private String valuebName = "";

    @JsonProperty("color")
    private String color;

    @JsonProperty("price")
    private String price;
....................................
....................................
}

 

Then after that we can create a Class for Content to let “type” files  work as a EXTERNAL_PROPERTY to control the Value object in side the Content:

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Content {

    @JsonProperty("type")
    private String type;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonProperty("value")
    private Value value = null;
    
    public Content (){
    }
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
    @JsonSubTypes({ @Type(value = ValueA.class, name = "valuea"),@Type(value = ValueB.class, name = "valueb")})
    public void setValue(Value value) {
        this.value = value;
    }

    public Value getValue() {
        return value;
    }

}

In this way. @JsonTypeInfo in fact define how to rely on “type” to dynamically  serialize and de-serialize the json/java object.

To make this properly work, your Jackson version must higher than the 2.5 version. As I have faced the duplicated fields bug in the 2.5  version Jackson , here is sample dependency:

 

        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.8.0</version>
        </dependency>

 

 

Advertisements

Spring MVC + Security Note (4) – Migrate from Spring Security 3.X to 4.X

By W.ZH

Refer to this link

http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-xml.html

Here are some notes that common change in page or configuration files:

  1. default URL for login and logout form action are changed:
    /j_spring_security_logout     to /logout
    /j_spring_security_check     to /login
    So we need to change our JSP content for these two.
  2. Default csrf is enabled… so if you want to be disabled, just add this
    <csrf disabled=”true”/> in the <http />tag
    If you want enable, you do not need to do sth.
  3. <access-denied-handler error-page=”/page/403″ />  put inside the <http />
    not like old on as the attribute <http *** />
  4. If you have more than one roles, you can not use the
    access=”ROLE_USER,ROLE_ADMIN”  any more, you have to change to<http auto-config=”true” use-expressions=”true” >
    …..
    <intercept-url  ……. access=”hasAnyRole(‘ROLE_USER’,’ROLE_ADMIN’)” …..you maybe have others need changes, need to refer to reference page

How to consume json object from Spring MVC controller input directly

By WZH

This is small note about the json input directly to Spring MVC controller side, jackson will auto convert it to Java object you want:

  1. Make sure has this jar added

 <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.0</version>
  </dependency>

2.  Controller side

@RequestMapping(value = urlPatternController, method = RequestMethod.POST)
public @ResponseBody Person createPerson(@RequestBody Person jsonString) {
   Person person=personService.savedata(jsonString);
   return person;
}

@RequestBody – Covert Json object to java
@ResponseBody– convert Java object to json

3. JS client side :

 

$.ajax("<%=path%>/web/urlPatternController", {
                           type:'POST',
                           dataType: 'json',
                           data: person
                    })

 

Link can be refered

 

 

A special case for de-serialize Polymorphic class by Jackson

A special case for de-serialize Polymorphic class by Jackson

 

By WZh   Apr 2015

A very good article about the de-serialize the data in Jackson from Json to JAVA is already at this article

  1. http://programmerbruce.blogspot.co.uk/2011/05/deserialize-json-with-jackson-into.html

But a special case we have met is not in that article. Input json sample:

//   {

“type”:”dog”,

//     “animal”:

//           {“name”:”Spike”,

// “breed”:”mutt”,

//            “leash_color”:”red”}

}

or

//   {

“type”:”cat”,

//     “animal”:

//           {“name”:”Fluffy”,

//           “favorite_toy”:”spider ring”}

//   }

This means type is not inside the abstract class itself, type is controlled by parent class fields and also we want to use getter and setter in the parent class for “type” in other java code, to type must be cleared defined at Zoo class as Java field.

Here is the solution after research!

class Zoo

{

private String type;

private Animal animal;  

@JsonTypeId

public String getType() {

return type;

}

public void setType(String type) {

this.type = type;

}

@JsonTypeInfo(

use = JsonTypeInfo.Id.NAME,

include = JsonTypeInfo.As.EXTERNAL_PROPERTY,

property = “type”)

@JsonSubTypes({     @Type(value = Cat.class, name = “cat”),  

    @Type(value = Dog.class, name = “dog”) })  

public void setAnimal ( Animal value) {

this.animal = value;

}

public Animal getAnimal () {

return animal;

}

}

abstract class Animal

{

}

class Dog extends Animal

{

  public String name;

  public String breed;

  public String leashColor;

}

class Cat extends Animal

{

  public String name;

  public String favoriteToy;

}

the most important thing in the code is :

  1. @JsonTypeId set to getter for “type” and let the jackson do not create duplicate “type” in the json
  2. @JsonTypeInfo defined with JsonTypeInfo.As.EXTERNAL_PROPERTY to let the Animal class can be controlled by the external property “type” to do the Polymorphic
  3. @JsonTypeInfo is put to the setter of the abstract class to contorl the Polymorphic

workarea SOAP header cause the SOAPFaultException

By W.ZH Dec 2014

Issue:

When I was working on a project about the WS, my client jar always get this error:

Get Eligibility failed. Severity level is 5. Msg=SOAPFaultException – FaultCode [{http://schemas.xmlsoap.org/soap/envelope/}Server] FaultString [15
] FaultActor [null] Detail [<detail><bea_fault:stacktrace xmlns:bea_fault=”http://www.bea.com/servers/wls70/webservice/fault/1.0.0″>java.lang.ArrayIndexOutOfBoundsException: 15
at com.sun.beans.ObjectHandler.dequeueResult(ObjectHandler.java:139)
at java.beans.XMLDecoder.readObject(XMLDecoder.java:201)
at weblogic.wsee.workarea.WorkContextXmlInputAdapter.readUTF(WorkContextXmlInputAdapter.java:111)
at weblogic.workarea.spi.WorkContextEntryImpl.readEntry(WorkContextEntryImpl.java:92)
at weblogic.workarea.WorkContextLocalMap.receiveResponse(WorkContextLocalMap.java:226)
at weblogic.workarea.WorkContextMapImpl.receiveResponse(WorkContextMapImpl.java:182)
at weblogic.wsee.workarea.WorkAreaClientHandler.handleResponse(WorkAreaClientHandler.java:49)
at weblogic.wsee.handler.HandlerIterator.handleResponse(HandlerIterator.java:287)
at weblogic.wsee.handler.HandlerIterator.handleResponse(HandlerIterator.java:271)
at weblogic.wsee.ws.dispatch.client.ClientDispatcher.handleResponse(ClientDispatcher.java:213)
at weblogic.wsee.ws.dispatch.client.ClientDispatcher.dispatch(ClientDispatcher.java:150)
at weblogic.wsee.ws.WsStub.invoke(WsStub.java:87)
at weblogic.wsee.jaxrpc.StubImpl._invoke(StubImpl.java:341)

When I look at the SOAP XML. found this in the header:

<work:WorkContext xmlns:wsu=”http://schemas.xmlsoap.org/ws/2002/07/utility&#8221;

                      xmlns:work=”http://bea.com/2004/06/soap/workarea/”&gt;

     <java class=”java.beans.XMLDecoder”>

      <string>**************</string>

      <int>214</int>

      <string>weblogic.workarea.StringWorkContext</string>

      <string>1.4.1.18</string>

      <string>oracle.dms.context.internal.wls.WLSContextFamily</string>

      <int>335</int>

      <string>weblogic.workarea.SerializableWorkContext</string>

      <int>356</int>

      <array class=”byte” length=”356″>

       <void index=”0″>

       ……………………………………..

      <boolean>false</boolean>

      <string/>

     </java>

    </work:WorkContext>

 

Reason:

As my jar client is legacy code, so it in fact can not read this extra header properly, so it will get error.

Solution:

Use this flag at your setDomainEnv.sh to change java flag for this extra header:

-Dweblogic.wsee.workarea.skipWorkAreaHeader=true

after restart server. it will be fine

Some cool tips of Jackson

Some cool tips of Jackson

from Ted M. Young

  • To make the JSON easier to read, you can tell ObjectMapper to use a “pretty printer”,

ObjectMapper objectMapper = new ObjectMapper();

Book jacksonCookbook = new JacksonCookbook();

String json = objectMapper

.writerWithDefaultPrettyPrinter()

.writeValueAsString(jacksonCookbook);

System.out.println(json);

 

If you always want to pretty-print the output, you can configure the ObjectMapper instance directly using the SerializationFeature class:


ObjectMapper objectMapper = new ObjectMapper();

objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

 

The enable method here turns on the INDENT_OUTPUT feature, which tells Jackson to use the DefaultPrettyPrinter when serializing your objects. The code

objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true)

  • If you have property values in your data that can be null, but don’t want them serialized when they are, you can do the following to make the setting global for all uses of this objectMapper reference:

objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

If you only want to do this on a per-class basis, you can add the @JsonInclude annotation above the class or interface definition:


@JsonSerialize(include = Inclusion.NON_NULL)
public class HasNullValues {private String optionalSecondAddress; // nullable field

 

  • Sometimes you’ll want the properties that in your JSON output to be in a specific order, perhaps because you have tests or clients that rely on the ordering (I’d try to avoid that, but sometimes it’snot under your control), or you want it to be easier to find a certain property when looking at the JSON during debugging.

To order on a per-class basis, use the @JsonPropertyOrder annotation:


@JsonPropertyOrder({“id”, “symbol”})
public class Currency {public String symbol;

public String shortName;

public String longName;

public long id;

}

This will ensure that the id property will be first, followed by the symbol property, with the rest in an undetermined order.

If you wanted them simply in alphabetic order, you would use the alphabetic parameter in the annotation:

@JsonPropertyOrder(alphabetic = true)

If you always want the properties sorted alphabetically, configure the ObjectMapper instance directly:

objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

Note that the above settings won’t affect the order of the contents of any maps that you have (e.g., a HashMap). If you want the output of maps to be ordered by their keys, you’ll need to turn on the ORDER_MAP_ENTRIES_BY_KEYS feature:

objectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);

local class incompatible: stream classdesc serialVersionUID = -4312319268131871162, local class serialVersionUID = -5312119737527277188

    By W.ZH  Aug 2014

Issue:

When we use some webservice , we get error like this :

java.io.InvalidClassException: com.abc.EmailInfo; local class incompatible: stream classdesc serialVersionUID = -4312319268131871162, local class serialVersionUID = -5312119737527277188

com.abc.EmailInfo is a java object is used in some code.

 

 

Reason:

com.abc.EmailInfo exist in fact in several jar files. each jaf file in fact contain a different version of the  EmailInfo  class, even class source code could lokks like same.

 

What we should do is:

1. find out all the jar files that contain this class file:  j1.jar and j2.jar such as.

2. use serialver to check the serialVersionUID of each class in each jar:

       C:\SVN\….WEB-INF\lib>serialver -classpath jar1.jar com.abc.EmailInfo

        com.abc.EmailInfo:    static final long serialVersionUID = -5312119737527277188L;

    

       C:\SVN\….WEB-INF\lib>serialver -classpath jar2.jar com.abc.EmailInfo

        com.abc.EmailInfo:    static final long serialVersionUID = -4312319268131871162L;

3. now you see the reason of the class with same name of diff version in fact will creat different serialVersionUID

4. so try to unzip one of jar file and then get class to update the other one, suppose the have same code for EmailInfo class, so they can exchange with each other.

5. use this command to update the class in one jar file:

C:\SVN\….WEB-INF\lib>jar uf jar1.jar com\abc\EmailInfo.class

Then you can try to see result.