Export JSON plugin page

Project: http://git-wip-us.apache.org/repos/asf/struts-site/repo
Commit: http://git-wip-us.apache.org/repos/asf/struts-site/commit/4221720d
Tree: http://git-wip-us.apache.org/repos/asf/struts-site/tree/4221720d
Diff: http://git-wip-us.apache.org/repos/asf/struts-site/diff/4221720d

Branch: refs/heads/master
Commit: 4221720d2c925c8e6dfff67558779020d07c60be
Parents: 271ed7b
Author: Lukasz Lenart <lukaszlen...@apache.org>
Authored: Tue Sep 12 12:00:23 2017 +0200
Committer: Lukasz Lenart <lukaszlen...@apache.org>
Committed: Tue Sep 12 12:01:10 2017 +0200

----------------------------------------------------------------------
 pom.xml                      |   4 +-
 source/plugins/json/index.md | 639 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 641 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/struts-site/blob/4221720d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 420bb63..30d6d8e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -94,14 +94,14 @@
                                         <argument>-a</argument>
                                         
<argument>${project.build.directory}/md/attachments</argument>
                                         <argument>-o</argument>
-                                        
<argument>${project.build.directory}/md/junit-plugin.md</argument>
+                                        
<argument>${project.build.directory}/md/json-plugin.md</argument>
                                         <argument>-u</argument>
                                         
<argument>${confluence.user}:${confluence.password}</argument>
                                         <argument>-server</argument>
                                         
<argument>https://cwiki.apache.org/confluence</argument>
                                         <argument>+gfm</argument>
                                         <argument>true</argument>
-                                        <argument>2330106</argument>
+                                        <argument>2850922</argument>
                                     </arguments>
                                 </configuration>
                             </execution>

http://git-wip-us.apache.org/repos/asf/struts-site/blob/4221720d/source/plugins/json/index.md
----------------------------------------------------------------------
diff --git a/source/plugins/json/index.md b/source/plugins/json/index.md
new file mode 100644
index 0000000..7586ccf
--- /dev/null
+++ b/source/plugins/json/index.md
@@ -0,0 +1,639 @@
+---
+layout: plugin
+title: Convention plugin
+---
+
+# JSON Plugin
+{:.no_toc}
+
+* Will be replaced with the ToC, excluding a header
+{:toc}
+
+## Description
+
+The JSON plugin provides a `json` result type that serializes actions into JSON
+
+1. The `content-type` must be `application/json`
+2. The JSON content must be well formed, see [json.org](http://www.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:
+
+```json
+{
+   "doubleValue": 10.10,
+   "nestedBean": {
+      "name": "Mr Bean"
+   },
+   "list": ["A", 10, 20.20, {
+      "firstName": "El Zorro"
+   }],
+   "array": [10, 20] 
+}
+```
+
+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.
+
+> So serialize your objects to JSON in javascript see 
[json2](http://json.org/json2.js).
+
+`root` attribute must be set on the `JSONInterceptor` when dealing with JSON 
array.
+
+This plugin also provides _AJAX Validation_.
+
+## 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:
+
+```xml
+<dependencies>
+   ...
+   <dependency>
+       <groupId>org.apache.struts</groupId>
+       <artifactId>struts2-json-plugin</artifactId>
+       <version>STRUTS_VERSION</version>
+   </dependency>
+   ...
+</dependencies>
+```
+
+## 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:
+
+```xml
+<!-- Result fragment -->
+<result type="json">
+  <param name="excludeProperties">
+    login.password,
+    studentList.*.sin
+  </param>
+</result>
+
+<!-- Interceptor fragment -->
+<interceptor-ref name="json">
+  <param name="enableSMD">true</param>
+  <param name="excludeProperties">
+    login.password,
+    studentList.*.sin
+  </param>
+</interceptor-ref>
+```
+
+### 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.
+
+> 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 property expression matches a property first.
+
+```xml
+<!-- Result fragment -->
+<result type="json">
+  <param name="includeProperties">
+    ^entries\[\d+\].clientNumber,
+    ^entries\[\d+\].scheduleNumber,
+    ^entries\[\d+\].createUserId
+  </param>
+</result>
+```
+
+### Root Object
+
+Use the `root` attribute (OGNL expression) to specify the root object to be 
serialized.
+
+```xml
+<result type="json">
+  <param name="root">
+    person.job
+  </param>
+</result>
+```
+
+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**.
+
+```xml
+<interceptor-ref name="json">
+  <param name="root">bean1.bean2</param>
+</interceptor-ref>
+```
+
+### 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:
+```xml
+<result type="json">
+  <param name="wrapPrefix">/*</param>
+  <param name="wrapSuffix">*/</param>
+</result>
+```
+
+- Add a prefix:
+```xml
+<result type="json">
+  <param name="wrapPrefix">{}&&</param>
+</result>
+```
+
+- Wrap for file upload:
+```xml
+<result type="json">
+  <param name="wrapPrefix"><![CDATA[<html><body><textarea>]]></param>
+  <param name="wrapSuffix"><![CDATA[</textarea></body></html>]]></param>
+</result>
+```
+
+### Wrap with Comments
+
+`wrapWithComments` is deprecated from 0.34, use `wrapPrefix` and `wrapSuffix` 
instead.
+
+> `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:
+
+```json
+/* {
+   "doubleVal": 10.10,
+   "nestedBean": {
+      "name": "Mr Bean"
+   },
+   "list": ["A", 10, 20.20, {
+      "firstName": "El Zorro"
+   }],
+   "array": [10, 20] 
+} */
+```
+To strip those comments use:
+
+```javascript
+var responseObject = eval("("+data.substring(data.indexOf("\/\*")+2, 
data.lastIndexOf("\*\/"))+")");
+```
+
+### Prefix
+
+`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](http://trac.dojotoolkit.org/ticket/6380) for 
details:
+
+```xml
+<result type="json">
+  <param name="prefix">true</param>
+</result>
+```
+
+### 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:
+
+```xml
+<result type="json">
+  <param name="ignoreHierarchy">false</param>
+</result>
+```
+
+### Enumerations
+
+By default, an Enum is serialized as a `name=value` pair where `value = 
name()`.
+
+```java
+  public enum AnEnum {
+     ValueA,
+     ValueB
+  }
+```
+
+```json  
+  "myEnum":"ValueA"
+```
+
+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.
+
+```java
+  public enum AnEnum {
+     ValueA("A"),
+     ValueB("B");
+
+     private String val;
+     
+     public AnEnum(val) {
+        this.val = val;
+     }
+     public getVal() {
+        return val;
+     }
+   }
+```
+
+```json
+  myEnum: { "_name": "ValueA", "val": "A" }
+```
+
+Enable this parameter through `struts.xml`:
+
+```xml
+<result type="json">
+  <param name="enumAsBean">true</param>
+</result>
+```
+
+### 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.
+
+```xml
+<result type="json">
+  <param name="enableGZIP">true</param>
+</result>
+```
+
+### 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`
+
+```xml
+<result type="json">
+  <param name="noCache">true</param>
+</result>
+```
+
+### 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.
+
+```xml
+<result type="json">
+  <param name="excludeNullProperties">true</param>
+</result>
+```
+
+### Status and Error code
+
+Use `statusCode` to set the status of the response:
+
+```xml
+<result type="json">
+  <param name="statusCode">304</param>
+</result>
+```
+
+And `errorCode` to send an error (the server might end up sending something to 
the client which is not the serialized JSON):
+
+```json
+<result type="json">
+  <param name="errorCode">404</param>
+</result>
+```
+
+### 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":
+
+```xml
+<result type="json">
+  <param name="callbackParameter">callback</param>
+</result>
+```
+
+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.
+
+```xml
+<result type="json">
+  <param name="contentType">text/html</param>
+</result>
+```
+
+### 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:
+
+```xml
+<result type="json">
+  <param name="encoding">UTF-8</param>
+</result>
+```
+
+## Example
+
+### Setup Action
+
+This simple action has some fields:
+
+Example:
+
+```java
+import java.util.HashMap;
+import java.util.Map;
+
+import com.opensymphony.xwork2.Action;
+
+public class JSONExample {
+    private String field1 = "str";
+    private int[] ints = {10, 20};
+    private Map map = new HashMap();
+    private String customName = "custom";
+
+    //'transient' fields are not serialized
+    private transient String field2;
+
+    //fields without getter method are not serialized
+    private String field3;
+
+    public String execute() {
+        map.put("John", "Galt");
+        return Action.SUCCESS;
+    }
+
+    public String getField1() {
+        return field1;
+    }
+
+    public void setField1(String field1) {
+        this.field1 = field1;
+    }
+
+    public int[] getInts() {
+        return ints;
+    }
+
+    public void setInts(int[] ints) {
+        this.ints = ints;
+    }
+
+    public Map getMap() {
+        return map;
+    }
+
+    public void setMap(Map map) {
+        this.map = map;
+    }
+
+    @JSON(name="newName")
+    public String getCustomName() {
+        return this.customName;
+    }
+}
+```
+
+### 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:
+
+```java
+import java.util.HashMap;
+import java.util.Map;
+
+import com.opensymphony.xwork2.ActionSupport;
+import org.apache.struts2.convention.annotation.Result;
+
+@Result(type = "json")
+public class JSONExample extends ActionSupport {
+// action code
+}
+```
+
+Example with XML Configuration:
+
+```xml
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE struts PUBLIC
+    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
+    "http://struts.apache.org/dtds/struts-2.0.dtd";>
+
+<struts>
+
+  <package name="example"  extends="json-default">
+     <action name="JSONExample" class="example.JSONExample">
+        <result type="json"/>
+     </action>
+  </package>
+
+</struts>
+```
+
+### JSON example output
+
+```json
+{  
+   "field1" : "str", 
+   "ints": [10, 20],
+   "map": {
+       "John":"Galt"
+   },
+   "newName": "custom"
+}
+```
+
+### Accepting JSON
+
+Your actions can accept incoming JSON if they are in package which uses 
`json` interceptor or by adding reference 
+to it as follow:
+
+```jaba
+@InterceptorRef(value="json")
+```
+
+By default `Content-Type` of value `application/json` is recognised to be 
used for de-serialisation 
+and `application/json-rpc` to execute SMD processing. You can override those 
settings be defining `jsonContentType`
+and `jsonRpcContentType` params, see example:
+
+```xml
+<interceptor-ref name="json">
+  <param name="jsonContentType">text/json</param>
+  <param name="jsonRpcContentType">text/json-rpc</param>
+</interceptor-ref>
+```
+
+Please be aware that those are scoped params per stack, which means, once set 
it will be used by actions in scope of this stack.
+
+## 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](http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book9)
+to advertise the remote service. Let's work it out with an example(useless as 
most examples).
+
+First write the action:
+
+```java
+package smd;
+
+import com.googlecode.jsonplugin.annotations.SMDMethod;
+import com.opensymphony.xwork2.Action;
+
+public class SMDAction {
+    public String smd() {
+        return Action.SUCCESS;
+    }
+    
+    @SMDMethod
+    public Bean doSomething(Bean bean, int quantity) {
+        bean.setPrice(quantity * 10);
+        return bean;
+    }
+}
+```
+
+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:
+
+```java
+package smd;
+
+public class Bean {
+    private String type;
+    private int price;
+    
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public void setPrice(int price) {
+        this.price = price;
+    }
+
+}
+```
+
+The mapping:
+
+```xml
+<package name="RPC" namespace="/nodecorate" extends="json-default">
+    <action name="SMDAction" class="smd.SMDAction" method="smd">
+        <interceptor-ref name="json">
+            <param name="enableSMD">true</param>
+        </interceptor-ref>
+        <result type="json">
+             <param name="enableSMD">true</param>
+        </result>
+    </action>
+</package>
+```
+
+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:
+
+```jsp
+<s:url id="smdUrl" namespace="/nodecorate" action="SMDAction" />
+<script type="text/javascript">
+    //load dojo RPC
+    dojo.require("dojo.rpc.*");
+    
+    //create service object(proxy) using SMD (generated by the json result)
+    var service = new dojo.rpc.JsonService("${smdUrl}");
+    
+    //function called when remote method returns
+    var callback = function(bean) {
+        alert("Price for " + bean.type + " is " + bean.price);
+    };
+    
+    //parameter
+    var bean = {type: "Mocca"};
+    
+    //execute remote method
+    var defered = service.doSomething(bean, 5);
+
+    //attach callback to defered object
+    defered.addCallback(callback);
+</script>
+```
+
+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.
+
+> 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.
+
+```xml
+<action name="contact" class="package.ContactAction" method="smd">
+   <interceptor-ref name="json">
+      <param name="enableSMD">true</param>
+      <param name="ignoreSMDMethodInterfaces">false</param>
+   </interceptor-ref>
+   <result type="json">
+      <param name="enableSMD">true</param>
+      <param name="ignoreInterfaces">false</param>
+   </result>
+   <interceptor-ref name="default"/>
+</action>
+```

Reply via email to