Package org.springside.examples.showcase.demos.utilities.json

Source Code of org.springside.examples.showcase.demos.utilities.json.JsonDemo$Views$Public

/*******************************************************************************
* Copyright (c) 2005, 2014 springside.github.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
*******************************************************************************/
package org.springside.examples.showcase.demos.utilities.json;

import static org.assertj.core.api.Assertions.*;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.junit.Test;
import org.springside.modules.mapper.JsonMapper;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
* 演示Jackson的基本使用方式及大量的特殊Feature.
*
* @author calvin
*/
public class JsonDemo {

  private static JsonMapper mapper = JsonMapper.nonDefaultMapper();

  // // 基本操作 演示 ////

  /**
   * 序列化对象/集合到Json字符串.
   */
  @Test
  public void toJson() throws Exception {
    // Bean
    TestBean bean = new TestBean("A");
    String beanString = mapper.toJson(bean);
    System.out.println("Bean:" + beanString);
    assertThat(beanString).isEqualTo("{\"name\":\"A\"}");

    // Map
    Map<String, Object> map = Maps.newLinkedHashMap();
    map.put("name", "A");
    map.put("age", 2);
    String mapString = mapper.toJson(map);
    System.out.println("Map:" + mapString);
    assertThat(mapString).isEqualTo("{\"name\":\"A\",\"age\":2}");

    // List<String>
    List<String> stringList = Lists.newArrayList("A", "B", "C");
    String listString = mapper.toJson(stringList);
    System.out.println("String List:" + listString);
    assertThat(listString).isEqualTo("[\"A\",\"B\",\"C\"]");

    // List<Bean>
    List<TestBean> beanList = Lists.newArrayList(new TestBean("A"), new TestBean("B"));
    String beanListString = mapper.toJson(beanList);
    System.out.println("Bean List:" + beanListString);
    assertThat(beanListString).isEqualTo("[{\"name\":\"A\"},{\"name\":\"B\"}]");

    // Bean[]
    TestBean[] beanArray = new TestBean[] { new TestBean("A"), new TestBean("B") };
    String beanArrayString = mapper.toJson(beanArray);
    System.out.println("Array List:" + beanArrayString);
    assertThat(beanArrayString).isEqualTo("[{\"name\":\"A\"},{\"name\":\"B\"}]");
  }

  /**
   * 从Json字符串反序列化对象/集合.
   */
  @Test
  public void fromJson() throws Exception {
    // Bean
    String beanString = "{\"name\":\"A\"}";
    TestBean bean = mapper.fromJson(beanString, TestBean.class);
    System.out.println("Bean:" + bean);

    // Map
    String mapString = "{\"name\":\"A\",\"age\":2}";
    Map<String, Object> map = mapper.fromJson(mapString, HashMap.class);
    System.out.println("Map:");
    for (Entry<String, Object> entry : map.entrySet()) {
      System.out.println(entry.getKey() + " " + entry.getValue());
    }

    // List<String>
    String listString = "[\"A\",\"B\",\"C\"]";
    List<String> stringList = mapper.fromJson(listString, List.class);
    System.out.println("String List:");
    for (String element : stringList) {
      System.out.println(element);
    }

    // List<Bean>
    String beanListString = "[{\"name\":\"A\"},{\"name\":\"B\"}]";
    JavaType beanListType = mapper.contructCollectionType(List.class, TestBean.class);
    List<TestBean> beanList = mapper.fromJson(beanListString, beanListType);
    System.out.println("Bean List:");
    for (TestBean element : beanList) {
      System.out.println(element);
    }
  }

  /**
   * 测试三种不同的Inclusion风格.
   */
  @Test
  public void threeTypeInclusion() {
    TestBean bean = new TestBean("A");

    // 打印全部属性
    JsonMapper normalMapper = new JsonMapper();
    assertThat(normalMapper.toJson(bean)).isEqualTo(
        "{\"name\":\"A\",\"defaultValue\":\"hello\",\"nullValue\":null}");

    // 不打印nullValue属性
    JsonMapper nonEmptyMapper = JsonMapper.nonEmptyMapper();
    assertThat(nonEmptyMapper.toJson(bean)).isEqualTo("{\"name\":\"A\",\"defaultValue\":\"hello\"}");

    // 不打印默认值未改变的nullValue与defaultValue属性
    JsonMapper nonDefaultMaper = JsonMapper.nonDefaultMapper();
    assertThat(nonDefaultMaper.toJson(bean)).isEqualTo("{\"name\":\"A\"}");
  }

  /*
   * 测试类似Jaxb的常用annotaion,如properName,ignore,propertyOrder
   */
  @Test
  public void jacksonAnnoation() {
    TestBean2 testBean = new TestBean2(1, "foo", 18);
    // 结果name属性输出在前,且被改名为productName,且age属性被ignore
    assertThat(mapper.toJson(testBean)).isEqualTo("{\"productName\":\"foo\",\"id\":1}");
  }

  /*
   * 测试直接使用Jaxb的annotaion
   */
  @Test
  public void jaxbAnnoation() {
    JsonMapper newMapper = new JsonMapper();
    newMapper.enableJaxbAnnotation();
    TestBean3 testBean = new TestBean3(1, "foo", 18);
    // 结果name属性输出在前,且被改名为productName,且age属性被ignore
    assertThat(newMapper.toJson(testBean)).isEqualTo("{\"productName\":\"foo\",\"id\":1}");
  }

  // 调转顺序
  @JsonPropertyOrder({ "name", "id" })
  public static class TestBean2 {

    public long id;

    @JsonProperty("productName")
    public String name;

    @JsonIgnore
    public int age;

    public TestBean2() {

    }

    public TestBean2(long id, String name, int age) {
      this.id = id;
      this.name = name;
      this.age = age;
    }

  }

  // 调转顺序
  @XmlType(propOrder = { "name", "id" })
  public static class TestBean3 {

    public long id;

    @XmlElement(name = "productName")
    public String name;

    @XmlTransient
    public int age;

    public TestBean3() {

    }

    public TestBean3(long id, String name, int age) {
      this.id = id;
      this.name = name;
      this.age = age;
    }

  }

  /**
   * 更新一個已存在Bean,JSON字符串裡只含有Bean的部分屬性,只覆蓋这部分的屬性.
   */
  @Test
  public void updateBean() {
    String jsonString = "{\"name\":\"A\"}";

    TestBean bean = new TestBean();
    bean.setDefaultValue("Foobar");

    mapper.update(jsonString, bean);

    // name被赋值
    assertThat(bean.getName()).isEqualTo("A");
    // DefaultValue不在Json串中,依然保留。
    assertThat(bean.getDefaultValue()).isEqualTo("Foobar");
  }

  /**
   * 測試輸出jsonp格式內容.
   */
  @Test
  public void jsonp() {
    TestBean bean = new TestBean("foo");
    assertThat(mapper.toJsonP("callback", bean)).isEqualTo("callback({\"name\":\"foo\"})");
  }

  /**
   * 演示用的Bean, 主要演示不同風格的Mapper對Null值,初始化後沒改變過的屬性值的處理.
   */
  public static class TestBean {

    private String name;
    private String defaultValue = "hello"; // 默认值没被修改过的属性,可能会不序列化
    private String nullValue = null; // 空值的据行,可能会不序列化

    public TestBean() {
    }

    public TestBean(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public String getDefaultValue() {
      return defaultValue;
    }

    public void setDefaultValue(String defaultValue) {
      this.defaultValue = defaultValue;
    }

    public String getNullValue() {
      return nullValue;
    }

    public void setNullValue(String nullValue) {
      this.nullValue = nullValue;
    }

    @Override
    public String toString() {
      return "TestBean [defaultValue=" + defaultValue + ", name=" + name + ", nullValue=" + nullValue + "]";
    }
  }

  // //特殊数据类型演示////

  /**
   * 测试对枚举的序列化.
   */
  @Test
  public void enumType() {
    // toJSon默認使用enum.name()
    assertThat(mapper.toJson(TestEnum.One)).isEqualTo("\"One\"");
    // fromJson使用enum.name()或enum.order()
    assertThat(mapper.fromJson("\"One\"", TestEnum.class)).isEqualTo(TestEnum.One);
    assertThat(mapper.fromJson("0", TestEnum.class)).isEqualTo(TestEnum.One);

    // 使用enum.toString(), 注意配置必須在所有讀寫動作之前調用.
    // 建议toString()使用index数字属性,比enum.name()节约了空间,比enum.order()则不会有顺序随时改变不确定的问题。
    JsonMapper newMapper = new JsonMapper();
    newMapper.enableEnumUseToString();
    assertThat(newMapper.toJson(TestEnum.One)).isEqualTo("\"1\"");
    assertThat(newMapper.fromJson("\"1\"", TestEnum.class)).isEqualTo(TestEnum.One);
  }

  /**
   * 枚舉類型的演示Bean.
   */
  public static enum TestEnum {
    One(1), Two(2), Three(3);

    private final int index;

    TestEnum(int index) {
      this.index = index;
    }

    @Override
    public String toString() {
      return String.valueOf(index);
    }
  }

  /**
   * 测试对日期的序列化,日期默认以Timestamp方式存储,也可以用2.0后也可以用@JsonFormat在属性上格式化.
   * 但JodaTime仍然只支持Timestamp形式, 或调用JodaTime.toString().
   */
  @Test
  public void dateType() {

    mapper.getMapper().registerModule(new JodaModule());

    Date date = new Date();
    DateTime dateTime = new DateTime(date);
    String timestampString = String.valueOf(date.getTime());
    String format = "yyyy-MM-dd HH:mm:ss";
    String formatedString = new DateTime(date).toString(format);

    DateBean dateBean = new DateBean();
    dateBean.startDate = date;
    dateBean.endDate = date;
    dateBean.dateTime = dateTime;

    // to json
    String expectedJson = "{\"startDate\":" + timestampString + ",\"endDate\":\"" + formatedString
        + "\",\"dateTime\":" + timestampString + "}";
    assertThat(mapper.toJson(dateBean)).isEqualTo(expectedJson);

    // from json
    Date expectedEndDate = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseDateTime(formatedString).toDate();

    DateBean resultBean = mapper.fromJson(expectedJson, DateBean.class);
    assertThat(resultBean.startDate).isEqualTo(date);
    assertThat(resultBean.endDate).isEqualTo(expectedEndDate);
  }

  public static class DateBean {
    // 默认timestamp存储
    public Date startDate;
    // 按annotation中的日期格式存储。
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")
    public Date endDate;

    public DateTime dateTime;
  }

  /**
   * 测试传入空对象,空字符串,Empty的集合,"null"字符串的结果.
   */
  @Test
  public void nullAndEmpty() {
    // toJson测试 //

    // Null Bean
    TestBean nullBean = null;
    String nullBeanString = mapper.toJson(nullBean);
    assertThat(nullBeanString).isEqualTo("null");

    // Empty List
    List<String> emptyList = Lists.newArrayList();
    String emptyListString = mapper.toJson(emptyList);
    assertThat(emptyListString).isEqualTo("[]");

    // fromJson测试 //

    // Null String for Bean
    TestBean nullBeanResult = mapper.fromJson(null, TestBean.class);
    assertThat(nullBeanResult).isNull();

    nullBeanResult = mapper.fromJson("null", TestBean.class);
    assertThat(nullBeanResult).isNull();

    // Null/Empty String for List
    List nullListResult = mapper.fromJson(null, List.class);
    assertThat(nullListResult).isNull();

    nullListResult = mapper.fromJson("null", List.class);
    assertThat(nullListResult).isNull();

    nullListResult = mapper.fromJson("[]", List.class);
    assertThat(nullListResult).isEmpty();
  }

  // // 高级应用 ////
  /**
   * 測試父子POJO間的循環引用.
   */
  @Test
  public void cycleReferenceBean() {
    // 初始化对象关系,parent的children里含有 child1,child2, child1/child2的parent均指向parent.
    CycleReferenceBean parent = new CycleReferenceBean("parent");

    CycleReferenceBean child1 = new CycleReferenceBean("child1");
    child1.setParent(parent);
    parent.getChildren().add(child1);

    CycleReferenceBean child2 = new CycleReferenceBean("child2");
    child2.setParent(parent);
    parent.getChildren().add(child2);

    // 序列化是, json字符串裡children中的child1/child2都不包含到parent的屬性
    String jsonString = "{\"name\":\"parent\",\"children\":[{\"name\":\"child1\"},{\"name\":\"child2\"}]}";
    assertThat(mapper.toJson(parent)).isEqualTo(jsonString);

    // 注意此時如果單獨序列化child1,也不會打印parent,信息將丟失。
    assertThat(mapper.toJson(child1)).isEqualTo("{\"name\":\"child1\"}");

    // 反向序列化时,Json已很聪明的把parent填入child1/child2中.
    CycleReferenceBean parentResult = mapper.fromJson(jsonString, CycleReferenceBean.class);
    assertThat(parentResult.getChildren().get(0).getParent().getName()).isEqualTo("parent");

    // 单独反序列化child1,当然parent也是空
    CycleReferenceBean child1Result = mapper.fromJson("{\"name\":\"child1\"}", CycleReferenceBean.class);
    assertThat(child1Result.parent).isNull();
    assertThat(child1Result.getName()).isEqualTo("child1");
  }

  /**
   * 父子POJO間的循環引用的演示Bean,@JsonBackReference 与 @JsonManagedReference 是关键.
   */
  public static class CycleReferenceBean {

    private String name;
    private CycleReferenceBean parent;
    private List<CycleReferenceBean> children = Lists.newArrayList();

    public CycleReferenceBean() {
    }

    public CycleReferenceBean(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    // 注意getter與setter都要添加annotation
    @JsonBackReference
    public CycleReferenceBean getParent() {
      return parent;
    }

    @JsonBackReference
    public void setParent(CycleReferenceBean parent) {
      this.parent = parent;
    }

    @JsonManagedReference
    public List<CycleReferenceBean> getChildren() {
      return children;
    }

    @JsonManagedReference
    public void setChildren(List<CycleReferenceBean> children) {
      this.children = children;
    }
  }

  /**
   * 測試可擴展Bean.
   * 可扩展Bean的设计会混合一些的固定属性和用一个Map<String,object>存放的扩展属性。
   * 通常,哪那些是固定属性,哪些是扩展属性,在应用不断演进中是不断变化的。
   * Jackson支持将所有属性都序列化成平行的属性列表,没有固定属性与Map中属性的区别,然后智能的将不在固定列的属性都丢到被@JsonAnyGetter/Setter注释的Map里面去。
   */
  @Test
  public void extensibleBean() {
    // 一个没有区分是变量还是Map的普通JSON字符串.
    String jsonString = "{\"name\" : \"Foobar\",\"age\" : 37,\"occupation\" : \"coder man\"}";
    ExtensibleBean extensibleBean = mapper.fromJson(jsonString, ExtensibleBean.class);
    // 固定属性
    assertThat(extensibleBean.getName()).isEqualTo("Foobar");
    assertThat(extensibleBean.getProperties().get("name")).isNull();

    // 可扩展属性
    assertThat(extensibleBean.getProperties().get("occupation")).isEqualTo("coder man");
  }

  /**
   * 演示用的可擴展Bean.@JsonAnySetter与@JsonAnyGetter是关键.
   */
  public static class ExtensibleBean {
    // 固定属性
    private String name;
    // 扩展属性
    private final Map<String, String> properties = Maps.newHashMap();

    public ExtensibleBean() {
    }

    @JsonAnySetter
    public void add(String key, String value) {
      properties.put(key, value);
    }

    @JsonAnyGetter
    public Map<String, String> getProperties() {
      return properties;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }
  }

  /**
   * 同一种POJO,在不同场景下可能需要序列化不同的属性组,Jackson支持使用View来定义.
   */
  @Test
  public void multiViewBean() throws IOException {
    MultiViewBean multiViewBean = new MultiViewBean();
    multiViewBean.setName("Foo");
    multiViewBean.setAge(16);
    multiViewBean.setOtherValue("others");

    // public view
    ObjectWriter publicWriter = mapper.getMapper().writerWithView(Views.Public.class);
    assertThat(publicWriter.writeValueAsString(multiViewBean)).isEqualTo(
        "{\"name\":\"Foo\",\"otherValue\":\"others\"}");

    // internal view
    ObjectWriter internalWriter = mapper.getMapper().writerWithView(Views.Internal.class);
    assertThat(internalWriter.writeValueAsString(multiViewBean))
        .isEqualTo("{\"age\":16,\"otherValue\":\"others\"}");

  }

  public static class Views {
    static class Public {
    }

    static class Internal {
    }
  }

  /**
   * 演示序列化不同View不同属性的Bean.
   */
  public static class MultiViewBean {
    private String name;
    private int age;
    private String otherValue;

    @JsonView(Views.Public.class)
    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    @JsonView(Views.Internal.class)
    public int getAge() {
      return age;
    }

    public void setAge(int age) {
      this.age = age;
    }

    public String getOtherValue() {
      return otherValue;
    }

    public void setOtherValue(String otherValue) {
      this.otherValue = otherValue;
    }
  }

  // //自定制行为////

  /**
   * 测试自定义转换器,整体感觉稍显复杂。这里是将Money和Long互转.
   */
  @Test
  public void customConverter() {

    JsonMapper newMapper = JsonMapper.nonEmptyMapper();

    SimpleModule moneyModule = new SimpleModule("MoneyModule");
    moneyModule.addSerializer(new MoneySerializer());
    moneyModule.addDeserializer(Money.class, new MoneyDeserializer());
    newMapper.getMapper().registerModule(moneyModule);

    // tojson
    User user = new User();
    user.setName("foo");
    user.setSalary(new Money(1.2));

    String jsonString = newMapper.toJson(user);

    assertThat(jsonString).isEqualTo("{\"name\":\"foo\",\"salary\":\"1.2\"}");

    // from
    User resultUser = newMapper.fromJson(jsonString, User.class);
    assertThat(resultUser.getSalary().value).isEqualTo(1.2);

  }

  public class MoneySerializer extends StdSerializer<Money> {
    public MoneySerializer() {
      super(Money.class);
    }

    @Override
    public void serialize(Money value, JsonGenerator jgen, SerializerProvider provider) throws IOException {

      jgen.writeString(value.toString());
    }
  }

  public class MoneyDeserializer extends StdDeserializer<Money> {
    public MoneyDeserializer() {
      super(Money.class);
    }

    @Override
    public Money deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
      return Money.valueOf(jp.getText());
    }

  }

  public static class Money {
    private final Double value;

    public Money(Double value) {
      this.value = value;
    }

    public static Money valueOf(String value) {
      Double srcValue = Double.valueOf(value);
      return new Money(srcValue);
    }

    @Override
    public String toString() {
      return value.toString();
    }
  }

  /**
   * 包含Money属性的对象.
   */
  public static class User {
    private String name;
    private Money salary;

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public Money getSalary() {
      return salary;
    }

    public void setSalary(Money salary) {
      this.salary = salary;
    }

  }

  /**
   * 测试修改 属性名策略。
   */
  @Test
  public void customPropertyNaming() throws JsonMappingException {

    TestBean bean = new TestBean("foo");
    bean.setDefaultValue("bar");
    JsonMapper newMapper = JsonMapper.nonEmptyMapper();
    newMapper.getMapper().setPropertyNamingStrategy(new LowerCaseNaming());
    assertThat(newMapper.toJson(bean)).isEqualTo("{\"name\":\"foo\",\"defaultvalue\":\"bar\"}");
  }

  public static class LowerCaseNaming extends PropertyNamingStrategy {
    @Override
    public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
      return defaultName.toLowerCase();
    }
  }
}
TOP

Related Classes of org.springside.examples.showcase.demos.utilities.json.JsonDemo$Views$Public

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.