JSON Plugin
Skip to end of metadata
Go to start of metadata

The JSON plugin provides a "json" result type that serializes actions into JSON. The serialization process is recursive, meaning that the whole object graph, starting on the action class (base class not included) will be serialized (root object can be customized using the "root" attribute). If the interceptor is used, the action will be populated from the JSON content in the request, these are the rules of the interceptor:

  1. The "content-type" must be "application/json"
  2. The JSON content must be well formed, see json.org for grammar.
  3. Action must have a public "setter" method for fields that must be populated.
  4. Supported types for population are: Primitives (int,long...String), Date, List, Map, Primitive Arrays, Other class (more on this later), and Array of Other class.
  5. Any object in JSON, that is to be populated inside a list, or a map, will be of type Map (mapping from properties to values), any whole number will be of type Long, any decimal number will be of type Double, and any array of type List.

Given this JSON string:

The action must have a "setDoubleValue" method, taking either a "float" or a "double" argument (the interceptor will convert the value to the right one). There must be a "setNestedBean" whose argument type can be any class, that has a "setName" method taking as argument an "String". There must be a "setList" method that takes a "List" as argument, that list will contain: "A" (String), 10 (Long), 20.20 (Double), Map ("firstName" -> "El Zorro"). The "setArray" method can take as parameter either a "List", or any numeric array.

Icon

So serialize your objects to JSON in javascript see json2

Installation

This plugin can be installed by copying the plugin jar into your application's /WEB-INF/lib directory. No other files need to be copied or created.

To use maven, add this to your pom:

Customizing Serialization and Deserialization

Use the JSON annotation to customize the serialization/deserialization process. Available JSON annotation fields:

Name

Description

Default Value

Serialization

Deserialization

name

Customize field name

empty

yes

no

serialize

Include in serialization

true

yes

no

deserialize

Include in deserialization

true

no

yes

format

Format used to format/parse a Date field

"yyyy-MM-dd'T'HH:mm:ss"

yes

yes

Excluding properties

A comma-delimited list of regular expressions can be passed to the JSON Result and Interceptor, properties matching any of these regular expressions will be ignored on the serialization process:

Including properties

A comma-delimited list of regular expressions can be passed to the JSON Result to restrict which properties will be serialized. ONLY properties matching any of these regular expressions will be included in the serialized output.

Note

Icon

Exclude property expressions take precedence over include property expressions. That is, if you use include and exclude property expressions on the same result, include property expressions will not be applied if an exclude exclude property expression matches a property first.

Root Object

Use the "root" attribute(OGNL expression) to specify the root object to be serialized.

The "root" attribute(OGNL expression) can also be used on the interceptor to specify the object that must be populated, make sure this object is not null.

Wrapping

For several reasons you might want to wrap the JSON output with some text, like wrapping with comments, adding a prefix, or to use file uploads which require the result to be wrapped in a textarea. Use wrapPrefix to add content in the beginning and wrapPostfix to add content at the end. This settings take precedence over "wrapWithComments" and "prefix" which are deprecated from 0.34 on. Examples:
Wrap with comments:

Add a prefix:

Wrap for file upload:

Wrap with Comments

Icon

wrapWithComments is deprecated from 0.34, use wrapPrefix and wrapSuffix instead.

Icon

wrapWithComments can turn safe JSON text into dangerous text. For example,

"*/ alert('XSS'); /*"

Thanks to Douglas Crockford for the tip!. Consider using prefix instead.

If the serialized JSON is {name: 'El Zorro'}. Then the output will be: {}&& ({name: 'El Zorro'}

If the "wrapWithComments" (false by default) attribute is set to true, the generated JSON is wrapped with comments like:

To strip those comments use:

Prefix

Icon

prefix is deprecated from 0.34, use wrapPrefix and wrapSuffix instead.

If the parameter prefix is set to true, the generated JSON will be prefixed with "{}&& ". This will help prevent hijacking. See this Dojo Ticket for details:

Base Classes

By default properties defined on base classes of the "root" object won't be serialized, to serialize properties in all base classes (up to Object) set "ignoreHierarchy" to false in the JSON result:

Enumerations

By default, an Enum is serialized as a name=value pair where value = name().

Use the "enumAsBean" result parameter to serialize Enum's as a bean with a special property _name with value name(). All properties of the enum are also serialized.

Enable this parameter through struts.xml:

Compressing the output.

Set the enableGZIP attribute to true to gzip the generated json response. The request must include "gzip" in the "Accept-Encoding" header for this to work.

Preventing the browser from caching the response

Set noCache to true(false by default) to set the following headers in the response:

  • Cache-Control: no-cache
  • Expires: 0
  • Pragma: No-cache

Excluding properties with null values

By default fields with null values are serialized like {property_name: null}. This can be prevented by setting excludeNullProperties to true.

Status and Error code

Use statusCode to set the status of the response:

And errorCode to send an error(the server might end up sending something to the client which is not the serialized JSON):

JSONP

To enable JSONP, set the parameter callbackParameter in either the JSON Result or the Interceptor. A parameter with that name will be read from the request, and it value will be used as the JSONP function. Assuming that a request is made with the parameter "callback"="exec":

And that the serialized JSON is {name: 'El Zorro'}. Then the output will be: exec({name: 'El Zorro'})

Content Type

Content type will be set to application/json-rpc by default if SMD is being used, or application/json otherwise. Sometimes it is necessary to set the content type to something else, like when uploading files with Dojo and YUI. Use the contentType parameter in those cases.

Encoding

User can define encoding per result or base on default assigned to struts.i18n.encoding. To define encoding for given result add encoding param as below:

Example

Setup Action

This simple action has some fields:

Example:

Write the mapping for the action

  1. Add the map inside a package that extends "json-default"
  2. Add a result of type "json"

Example with Convention Plugin Configuration:

Example with XML Configuration:

JSON example output

JSON RPC

The json plugin can be used to execute action methods from javascript and return the output. This feature was developed with Dojo in mind, so it uses Simple Method Definition to advertise the remote service. Let's work it out with an example(useless as most examples).

First write the action:

Methods that will be called remotely must be annotated with the SMDMethod annotation, for security reasons. The method will take a bean object, modify its price and return it. The action can be annotated with the SMD annotation to customize the generated SMD (more on that soon), and parameters can be annotated with SMDMethodParameter. As you can see, we have a "dummy", smd method. This method will be used to generate the Simple Method Definition (a definition of all the services provided by this class), using the "json" result.

The bean class:

The mapping:

Nothing special here, except that both the interceptor and the result must be applied to the action, and "enableSMD" must be enabled for both.

Now the javascript code:

Dojo's JsonService will make a request to the action to load the SMD, which will return a JSON object with the definition of the available remote methods, using that information Dojo creates a "proxy" for those methods. Because of the asynchronous nature of the request, when the method is executed, a deferred object is returned, to which a callback function can be attached. The callback function will receive as a parameter the object returned from your action. That's it.

Proxied objects

As annotations are not inherited in Java, some user might experience problems while trying to serialize objects that are proxied. eg. when you have attached AOP interceptors to your action.

In this situation, the plugin will not detect the annotations on methods in your action.

To overcome this, set the "ignoreInterfaces" result parameter to false (true by default) to request that the plugin inspects all interfaces and superclasses of the action for annotations on the action's methods.

NOTE: This parameter should only be set to false if your action could be a proxy as there is a performance cost caused by recursion through the interfaces.

Labels
  1. Anonymous

    Hi Musachy,

    trying to install the plug in using Maven 2 result in error.

    Unfortunately the dependecy you give:

    <dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-json-plugin</artifactId>
    <version>STRUTS_VERSION</version>
    </dependency>

    does not exists so it does not work.

    Can you provide a valid Maven repository please?

    Thank you.

    1. Anonymous

      Where it says, "STRUTS_VERSION" you have to type in the Struts version you are using. I'm using 2.3.4.1 so that's what I would type in.

  2. Hi Musachy,
    We are using this plugin with our application; and it works very well. Although; I had a question about date formatting.
    I see that here you have provided some documentation about how to format dates using annotations:

    @JSON(name="last_updated", format="MM/dd/yyyy hh:mm")
    public Date getLastUpdateDate()

    Unknown macro: { return this.lastUpdateDate; }

    However, if we want the format to change based on the locale, then this solutiuon will not work.

    So my question is : is there a way to externalize the format to a properties file so that we can pull the format using a key for example?

    Thanks for your help!
    -Shariva.
    shariva.verma@yahoo.com

  3. Anonymous

    I can not find the url of the mentioned jar
    what a pity.

    1. Anonymous

      struts-all.zip has included the mentioned jar.

  4. Anonymous

    Can somebody please explain the purpose/use of SMD & RPC. I am trying to use JSON with Jquery and is SMD necessary in this case?
    thanks
    V

  5. Anonymous

    is this a bug?

    <package name="x" extends="json-default">
            <action name="t" method="doit">
                <result type="json">
                    <param name="includeProperties">b</param>
                </result>
            </action>
    </package>

    in the action: Integer[] b = new Integer[]

    Unknown macro: {1,2,3,4}

    ;  and have get,set methods.

    when i config result like this:

    <result type="json">
       <param name="includeProperties">b</param>
    </result>

    this output is:

    Unknown macro: {"b"}

    this "b" is empty!!

    when i config result like this:

    <result type="json">
       <param name="root">b</param>
    </result>

    this output is: [1,2,3,4]

    this is the right data.

    if i don't config any param form the resutl tag. every variable Will correctly output.

    so,,,, it seems somthing wrong with the includeProperties tag.

    how should i do?

    1. You should ask it on the Struts user mailing list instead of here in the wiki, where no-one reads the questions in the comments.

  6. Anonymous

    wh

    when a string like this:
    String url = "http://www.google.com"

    why it is serialized as

    Unknown macro: {"url"}

    ??

  7. Anonymous

    when a string like this:
    String url = "http://www.google.com"

    why it is serialized as {"url":"http:\/\/www.google.com"}???

    1. Please ask questions on the struts user mailing list (or JSON mailing list, for the matter).

  8. Anonymous

    when use config :

    <result type="json">
    <param name="enableGZIP">true</param>
    </result>

    I found a encode bug,

    //code form here,in JSONUtil.java
    in = new ByteArrayInputStream(json.getBytes());

    when my local encode is GBK,but my project encode is UTF-8,

    I think should be
    in = new ByteArrayInputStream(json.getBytes(serializationParams.getEncoding()));

    1. Thanks, please report this bug at issues.apache.org, not here in the wiki.

  9. Anonymous

    What is the action's scope? singleton? If so, how to solve the concurrency problem?

    1. Actions are always prototypes, instantiated for every request.

  10. Anonymous

    how to deserialize JSON to JAVA object?

    1. Anonymous

      such as private List<Store> stores;

      the data pass to server is {stores : [

      Unknown macro: {id}

      ]}

      but the attributes of the store object are null. why?

      1. Please ask questions on the Struts user mailing list. The wiki is not the place to do so.

  11. Anonymous

    hi ,
    I have some problém with the struts2-json.
    ----------------Class Java -------------------------
    class UsersAction extend AbstractAction{
    }

    class abstract AbstractAction extend ActionSupport{
    private List<Users> _ListBean; /* getter + setter */
    public String List()

    Unknown macro: { _ListBean = _ObjectServices.getListAll(); log.info(_ListBean.size()); // _ListBean.size()=6 return SUCCESS; }

    }
    Class User{
    int id;
    String name;
    Set<friend> friends;
    }
    ---------------- Config file -------------------------

    <package name="json" namespace="/json" extends="json-default">
    <action name="ListUser" class="UsersAction" method="List">
    <result name="success" type="json">
    <param name="ignoreHierarchy">false</param>
    </result>
    </action>
    </package>

    but when I appele : my action /json/Uses.List return json empty "{}"

    1. Anonymous

      same problem with my code also

  12. Anonymous

  13. Anonymous

    struts json config <param name="defaultEncoding">utf-8</param> not working !!! why?

    1. try UTF-8 (uppercase)

  14. Anonymous

    my jsp can't get jsonValue ,but,if i remove method "getRequest()",it was well,why?can you talk about serialize detail.

    1. Anonymous

      ps:the method "getRequest()" was in my action

      1. Hard to spot where is the problem, could you ask your question on the user mailing list user@struts.apache.org ?

  15. When dealing with Long identifiers, an option to define a includeProperties param as a string would be very useful as javascript is unable to handle 64 bit integers.

    My workaround was to write an additional getter for Longs (i.e. getUserIdString) and the plugin worked perfectly (it wrapped the long in quites making javascript interpret it as a string) but a more intuitive and less repetitive approach would have been to somehow cast the parameter as a string in the action's result property.

    That said this is the only workaround I have had to put together to use this plugin...so, awesome plugin for an awesome framework, keep up the great work.

    Brad

    1. The simplest way is to request an improvement via https://issues.apache.org/jira/browse/WW (smile)