文章目录

  • Json数据和Java的相互转换
  • Java to JSON and JSON to Java
  • 使用ObjectMapper对象实现Java对象转换为JSON和JSON转换为Java
  • 通过Tree模型实现Java和JSON的转换
  • 通过流创建JSON


Json数据和Java的相互转换

Java to JSON and JSON to Java

Jackson 提供了可以被用来将Java对象转换为JSON以及反过来的类。在这个栗子中,我们将看看如何将Java对象转换为JSON以及如何将JSON转换为Java。我们将以一个简单的类开始并且慢慢提高它的复杂程度;假设我们是一家音乐公司并且我们希望发布一个可以查询唱片集的API。我们先建一个只有一个属性的Album类:

class Album {
    private String title;
    public Album(String title) {
        this.title = title;
    }
    public String getTitle() {
        return title;
    }
}

使用ObjectMapper对象实现Java对象转换为JSON和JSON转换为Java

我们使用ObjectMapper来完成JSON和对象之间的转换。默认情况下,Jackson将使用BeanSerializer 来序列化POJO。这是目前为止,我们的序列化示例的样子。注意,bean的私有字段需要有getter方法否则属性应该为共有的。json字符串如下:

{"title":"Kind Of Blue"}

添加一组链接到album(同样添加getter以及setter方法),它们指向新闻稿或者唱片的评论;

private String[] links;

在Main方法里添加如下代码:

album.setLinks(new String[] { "link1", "link2" });

此时,我们的JSON如下:注意,数组被转换为JSON数组(被[]包围);

{"title":"Kind Of Blue","links":["link1","link2"]}

接下来我们添加一组歌到唱片集里:

List<string> Songs = new ArrayList<string>();
songs.add("So What");
songs.add("Flamenco Sketches");
songs.add("Freddie Freeloader");
album.setSongs(songs);

最后,我们的JSON是这一个样子的,注意,List也被转换为JSON Array:

{"title":"Kind Of Blue","links":["link1","link2"],
"songs":["So What","Flamenco Sketches","Freddie Freeloader"]}

接下来,让我们在相册中添加一个艺术家。Artist是一个类,该类包含艺术家名称和由日期实例表示的艺术家的生日。注意,Artist是一个对象,因此JSON中包含“{”和“}”。这些大括号是对象的JSON表示形式,包含键值对。为了简单起见,我们将艺术家属性声明为public。

class Artist {
    public String name;
    public Date birthDate;
}

让我们把Artist添加到Album中

Artist artist = new Artist();
artist.name = "Miles Davis";
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
artist.birthDate = format.parse("26-05-1926");
album.setArtist(artist);

JSON结果如下:

{"title":"Kind Of Blue","links":["link1","link2"],
"songs":["So What","Flamenco Sketches","Freddie Freeloader"],
"artist":{"name":"Miles Davis","birthDate":-1376027600000}}

为了让JSON更加可读,我们添加这一行代码,注意,它在生产环境里不应该存在,只应该存在于测试或者开发环境里,因为这会增加json的大小;

mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

JSON将被很好地格式化:

{
  "title" : "Kind Of Blue",
  "links" : [ "link1" , "link2" ],
  "songs" : [ "So What", "Flamenco Sketches", "Freddie Freeloader" ],
  "artist" : {
    "name" : "Miles Davis",
    "birthDate" : -1376027600000
  }
}

我们接下来将在Album里添加一个包含音乐家以及他们所使用的乐器的Map:

private Map<String,String> musicians = new HashMap<String,String>();
public Map<String,String> getMusicians() {
        return Collections.unmodifiableMap(musicians);
}
public void addMusician(String key, String value){
        musicians.put(key, value);
}

在Main方法里添加:

album.addMusician("Miles Davis", "Trumpet, Band leader");
album.addMusician("Julian Adderley", "Alto Saxophone");
album.addMusician("Paul Chambers", "double bass");

JSON结果:

{
  "title" : "Kind Of Blue",
  "links" : [ "link1", "link2" ],
  "songs" : [ "So What", "Flamenco Sketches", "Freddie Freeloader" ],
  "artist" : {
    "name" : "Miles Davis",
    "birthDate" : -1376027600000
  },
  "musicians" : {
    "Julian Adderley" : "Alto Saxophone",
    "Paul Chambers" : "double bass",
    "Miles Davis" : "Trumpet, Band leader"
  }
}

让我们给转换添加更多的特性吧。我们首先告诉Mapper使用自然顺序排列Key:

mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

时间被默认格式化为纪元时间。让我们将它格式化为更加可读的格式吧:

SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMM yyyy");
mapper.setDateFormat(outputFormat);

默认情况下,Jackson使用java字段的名字作为Json的属性。你可以通过这篇教程里的注解改变这一行为。然而有些时候你可能无法修改java bean或者你不想将java bean和jackson注解绑定到一块。在这种情况下,jackson提供了一种改变默认字段的方法。使用Mapper的setPropertyNamingStrategy方法,设置它的字段命名策略。你可以重写nameForField方法或者nameForGetterMethod方法,这取决于你在bean里的属性还是getter方法是公有的。在这个栗子里,让我们将Album的“title”字段改为"Album-Title",“name"属性改为"Album-Name”。在main方法里我们需要做如下改变:

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
	@Override
	public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
   		if (field.getFullName().equals("com.studytrails.json.jackson.Artist#name"))
        	return "Artist-Name";
        return super.nameForField(config, field, defaultName);
	}
	@Override
	public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
  		if (method.getAnnotated().getDeclaringClass().equals(Album.class) && defaultName.equals("title"))
        	return "Album-Title";
        return super.nameForGetterMethod(config, method, defaultName);
  	}
});

Json现在是这样的:

{
  "Album-Title" : "Kind Of Blue",
  "links" : [ "link1", "link2" ],
  "songs" : [ "So What", "Flamenco Sketches", "Freddie Freeloader" ],
  "artist" : {
    "Artist-Name" : "Miles Davis",
    "birthDate" : "26 May 1926"
  },
  "musicians" : {
    "Julian Adderley" : "Alto Saxophone",
    "Miles Davis" : "Trumpet, Band leader",
    "Paul Chambers" : "double bass"
  }
}

让我们看看Jackson如何处理null的。我们添加三个新的属性到Artist类中,但是不要给它们赋值。我们添加了age、homeTown和awardWor:

public int age;
public String homeTown;
public List<String> awardsWon = new ArrayList<String>();

这是这部分Json的样子:

"age" : 0,
"homeTown" : null,
"awardsWon" : [ ]

我们使用这样的配置以告诉Jackson忽略空的属性:

mapper.setSerializationInclusion(Include.NON_EMPTY);

下面是全部的栗子:

public static void main(String[] args) throws IOException, ParseException {
        ObjectMapper mapper = new ObjectMapper();
        Album album = new Album("Kind Of Blue");
        album.setLinks(new String[] { "link1", "link2" });
        List songs = new ArrayList();
        songs.add("So What");
        songs.add("Flamenco Sketches");
        songs.add("Freddie Freeloader");
        album.setSongs(songs);
        Artist artist = new Artist();
        artist.name = "Miles Davis";
        SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
        artist.birthDate = format.parse("26-05-1926");
        album.setArtist(artist);
        album.addMusician("Miles Davis", "Trumpet, Band leader");
        album.addMusician("Julian Adderley", "Alto Saxophone");
        album.addMusician("Paul Chambers", "double bass");
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
        SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMM yyyy");
        mapper.setDateFormat(outputFormat);
        mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
            @Override
            public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
                if (field.getFullName().equals("com.studytrails.json.jackson.Artist#name"))
                    return "Artist-Name";
                return super.nameForField(config, field, defaultName);
            }
 
            @Override
            public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
                if (method.getAnnotated().getDeclaringClass().equals(Album.class) && defaultName.equals("title"))
                    return "Album-Title";
                return super.nameForGetterMethod(config, method, defaultName);
            }
        });
        mapper.setSerializationInclusion(Include.NON_EMPTY);
        mapper.writeValue(System.out, album);
    }
}
 
class Album {
    private String title;
    private String[] links;
    private List songs = new ArrayList();
    private Artist artist;
    private Map musicians = new HashMap();
 
    public Album(String title) {
        this.title = title;
    }
    public String getTitle() {
        return title;
    }
    public void setLinks(String[] links) {
        this.links = links;
    }
    public String[] getLinks() {
        return links;
    }
    public void setSongs(List songs) {
        this.songs = songs;
    }
    public List getSongs() {
        return songs;
    }
    public void setArtist(Artist artist) {
        this.artist = artist;
    }
    public Artist getArtist() {
        return artist;
    }
    public Map getMusicians() {
        return Collections.unmodifiableMap(musicians);
    }
    public void addMusician(String key, String value) {
        musicians.put(key, value);
    }
}
class Artist {
    public String name;
    public Date birthDate;
    public int age;
    public String homeTown;
    public List awardsWon = new ArrayList();
}

这是讲解如何将一个java对象转换为json教程的第一部分。下一部分我们将看到如何使用树来构建JSON。

通过Tree模型实现Java和JSON的转换

通过简单的树模型也能构建JSON。如果你不想为JSON结构写一些类,这可能就很有用。我们将使用上面的例子,album拥有一个song的数组,一个artist对象,一个musicians的数组。你需要做以下几点来创建一棵树:

  1. 创建一个JsonNodeFactory以创造节点;
  2. 通过JsonNodeFactory创建一个JsonGenerator并且指定输出方法。在这个栗子里,我们将把它写入控制台;
  3. 创建一个使用2中JsonGenerator以及根节点的ObjectMapper构建JSON;

当准备好之后,我们需要为album创建一个单根节点。注意默认情况下,ObjectMapper没有为根节点命名。

public static void main(String[] args) throws IOException {
	// Create the node factory that gives us nodes.
	JsonNodeFactory factory = new JsonNodeFactory(false);
 
	// create a json factory to write the treenode as json. for the example
	// we just write to console
	JsonFactory jsonFactory = new JsonFactory();
	JsonGenerator generator = jsonFactory.createGenerator(System.out);
	ObjectMapper mapper = new ObjectMapper();
	// the root node - album
	JsonNode album = factory.objectNode();
	mapper.writeTree(generator, album);
}

输出为:{}。为了打出空的大括号,用了这么多行代码!但是,接下来我们将通过这些代码创建Json。我们首先为album添加属性:

album.put("Album-Title", "Kind Of Blue");
//json输出:
{"Album-Title":"Kind Of Blue"}

添加链接数组:

ArrayNode links = factory.arrayNode();
links.add("link1").add("link2");
album.put("links", links);
//json输出
{"Album-Title":"Kind Of Blue","links":["link1","link2"]}

添加artist对象。注意artist对象本身也是一个JsonObject。

ObjectNode artist = factory.objectNode();
artist.put("Artist-Name", "Miles Davis");
artist.put("birthDate", "26 May 1926");
album.put("artist", artist);
//json输出
{"Album-Title":"Kind Of Blue","links":["link1","link2"],
"artist":{"Artist-Name":"Miles Davis","birthDate":"26 May 1926"}}

我们不添加音乐家。音乐家不在数组中,但有一个音乐家类型的对象。因此,我们创建一个对象。

ObjectNode musicians = factory.objectNode();
musicians.put("Julian Adderley", "Alto Saxophone");
musicians.put("Miles Davis", "Trumpet, Band leader");
album.put("musicians", musicians);
//json输出
{"Album-Title":"Kind Of Blue","links":["link1","link2"],
"artist":{"Artist-Name":"Miles Davis","birthDate":"26 May 1926"},
"musicians":{"Julian Adderley":"Alto Saxophone","Miles Davis":"Trumpet, Band leader"}

同样的,你可以添加其他元素。如果你有多个album,你可以在一个for循环里创建它们。一般来说通过Java对象来构建JSON性能更好。