GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Java library Json Hướng dẫn sử dụng Gson Annotations

Hướng dẫn sử dụng Gson Annotations

Đăng vào 12/01/2018 . Được đăng bởi GP Coder . 8559 Lượt xem . Toàn màn hình

Gson cung cấp một tập hợp các Annotation (chú thích) để đơn giản hóa quá trình Serialization và Deserialisation. Trong bài này, tôi sẽ hướng dẫn các bạn sử dụng các chú thích này như thế nào và làm thế nào chúng có thể đơn giản hóa việc sử dụng Gson để chuyển đổi giữa các đối tượng Java và các đối tượng JSON.

Nội dung

  • 1 Tùy chỉnh tên field sử dụng @SerializedName
  • 2 Loại trừ field sử dụng @Expose
  • 3 Quản lý phiên bản với @Since và @Until
  • 4 Áp dụng tùy chỉnh TypeAdapter, JsonSerializer, JsonDeserializer với @JsonAdapter

Tùy chỉnh tên field sử dụng @SerializedName

Hãy xem ví dụ dưới đây:


package com.gpcoder.gson.object;

import com.google.gson.annotations.SerializedName;

public class Student {

	@SerializedName("id")
	private int studentId;

	@SerializedName("name")
	private String studentName;

	@SerializedName("clazz")
	private String clazzId;

	public Student() {
		super();
	}

	public Student(int studentId, String studentName, String clazzId) {
		super();
		this.studentId = studentId;
		this.studentName = studentName;
		this.clazzId = clazzId;
	}

	@Override
	public String toString() {
		return "Student [studentId=" + studentId + ", studentName=" + studentName + ", clazzId=" + clazzId + "]";
	}

}


Trong ví dụ trên, các trường của lớp Student được chú thích với chú thích @SerializedName. Tham số (value) của chú thích này là tên được sử dụng khi thực hiện Serialization và Deserialisation các java object. Ví dụ, trường studentId trong Java được đại diện là id trong JSON.

Gson hỗ trợ chú thích này mà không cần thêm bất kỳ cấu hình nào, hãy xem cách sử dụng như bên dưới:


package com.gpcoder.gson.annotation;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.gpcoder.gson.object.Student;

public class SerializedNameExample {

	public static void main(String[] args) {
		final GsonBuilder builder = new GsonBuilder();
	    final Gson gson = builder.create();

	    final Student student = new Student(1, "GP Coder", "Java Dev");

	    final String json = gson.toJson(student);
	    System.out.println("Json: " + json);

	    final Student student2 = gson.fromJson(json, Student.class);
	    System.out.println("Java Object: " + student2);
	}
	
}

Kết quả thực thi chương trình trên:


Json: {"id":1,"name":"GP Coder","clazz":"Java Dev"}
Java Object: Student [studentId=1, studentName=GP Coder, clazzId=Java Dev]

Như bạn thấy giá trị của studentId trong java object tương ứng với key id trong chuỗi Json. Tương tự, studentName, clazzId tương ứng với key name và clazz trong chuỗi Json.

Loại trừ field sử dụng @Expose

Gson cung cấp hai loại chuyển đổi: Serialization (từ Java sang JSON) và Deserialisation (từ JSON sang Java). Các trường Java được đánh dấu transient (tạm thời) được loại trừ khỏi cả Serialization và Deserialisation . Do đó, thông tin không cần thiết có thể được đánh dấu là transient và Gson sẽ không được Serialization đến JSON.


public class User {  
    private String name;
    private int age;
    private boolean transient password; // will not be serialized or deserialized
}

Với Gson chúng ta có thể kiểm soát những gì được Serialization và Deserialisation độc lập bằng cách sử dụng chỉ chú thích (Annotation). Ngoài ra, chúng ta cũng có thể sử dụng một tùy chỉnh JsonDeserializer<T> và một tùy chỉnh JsonSerializer<T> để loại trừ các field (phần này sẽ được giới thiệu ở các bài viết kế tiếp). Mặc dù các giao diện này cung cấp khả năng kiểm soát và tính linh hoạt hoàn toàn nhưng cách tiếp cận chú thích được mô tả trong bài viết này đơn giản hơn vì nó không đòi hỏi các lớp bổ sung. Hãy xem ví dụ sau:


package com.gpcoder.gson.object;

import com.google.gson.annotations.Expose;

public class Account {
	@Expose(deserialize = false)
	private String accountNumber;

	@Expose
	private String iban;

	@Expose(serialize = false)
	private String owner;

	@Expose(serialize = false, deserialize = false)
	private String address;

	private String pin;

	public String getAccountNumber() {
		return accountNumber;
	}

	public void setAccountNumber(String accountNumber) {
		this.accountNumber = accountNumber;
	}

	public String getIban() {
		return iban;
	}

	public void setIban(String iban) {
		this.iban = iban;
	}

	public String getOwner() {
		return owner;
	}

	public void setOwner(String owner) {
		this.owner = owner;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getPin() {
		return pin;
	}

	public void setPin(String pin) {
		this.pin = pin;
	}

}

Ở ví dụ trên, tôi sử dụng @Expose có hai thuộc tính tùy chọn: deserialize và serialize. Thông qua hai thuộc tính này chúng ta có thể kiểm soát những gì được Serialization và Deserialisation. Các giá trị mặc định cho hai thuộc tính đều là true.

Để có thể làm việc với chú thích này, chúng ta cần phải sử dụng cấu hình phù hợp. Khác với chú giải @SerializedName, chúng ta cần phải cấu hình Gson chỉ Serialization và Deserialisation các trường được chú thích và bỏ qua phần còn lại như thể hiện trong đoạn mã sau đây:


package com.gpcoder.gson.annotation;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.gpcoder.gson.object.Account;

public class ExposeExample {
	public static void main(final String[] args) throws Exception {
		final GsonBuilder builder = new GsonBuilder();
		builder.excludeFieldsWithoutExposeAnnotation();
		final Gson gson = builder.create();

		final Account account = new Account();
		account.setAccountNumber("A123 4567 8909");
		account.setIban("IB 11 22 33 44");
		account.setOwner("GP Coder");
		account.setPin("123456");
		account.setAddress("Can Tho, Viet Nam");

		final String json = gson.toJson(account);
		System.out.println("Json: " + json);

		String json2 = "{\"accountNumber\":\"A123 4567 8909\",\"iban\":\"IBAN 11 22 33 44\",\"owner\":\"GP Coder\",\"pin\":\"123456\",\"address\":\"Can Tho, Viet Nam\"}";
		final Account account2 = gson.fromJson(json2, Account.class);
		System.out.println("Java Object: ");
		System.out.printf("  + Account Number: %s%n", account2.getAccountNumber());
		System.out.printf("  + IBAN:           %s%n", account2.getIban());
		System.out.printf("  + Owner:          %s%n", account2.getOwner());
		System.out.printf("  + Pin:            %s%n", account2.getPin());
		System.out.printf("  + Address:        %s%n", account2.getAddress());
	}
}

Kết quả thực thi chương trình trên:


Json: {"accountNumber":"A123 4567 8909","iban":"IB 11 22 33 44"}
Java Object: 
 + Account Number: null
 + IBAN: IBAN 11 22 33 44
 + Owner: GP Coder
 + Pin: null
 + Address: null

Như bạn thấy, Khi thực hiện Serialization chỉ accountNumber và iban được chuyển đổi từ Java Object sang chuỗi Json. Khi thực hiện Deserialisation chỉ owner và iban được chuyển đổi từ Json sang Java Object.

Quản lý phiên bản với @Since và @Until

Gson cung cấp hai chú thích được gọi là @Since và @Until có thể được sử dụng để kiểm soát các field khi chuyển đổi giữa các đối tượng Java và các đối tượng JSON.

Hai chú thích này hữu ích khi bạn muốn quản lý phiên bản của các lớp Json của mình. Bạn có thể kiểm soát nếu một field cụ thể thể được xem xét để serialization / deserialization dựa trên phiên bản nhất định.

  • Chú thích @Since : cho biết số phiên bản mà một field đã được thêm vào. Nghĩa là field này sẽ chỉ được xem xét cho Serialization / deserialization bắt đầu từ phiên bản nhất định. Trước phiên bản đó, nó sẽ bị bỏ qua.
  • Chú thích @Until : cho biết số phiên bản mà một field đã bị loại bỏ. Nghĩa là field này sẽ chỉ được xem xét cho Serialization / deserialization cho đến khi phiên bản nhất định. Sau phiên bản đó, nó sẽ bị bỏ qua.

Những chú thích này được áp dụng trên cấp field và chỉ có hiệu lực khi Gson được tạo bằng GsonBuilder và gọi phương thức GsonBuilder.setVersion() như dưới đây:


Gson gson = new GsonBuilder().setVersion(double).create();

Ví dụ:


package com.gpcoder.gson.object;

import com.google.gson.annotations.Since;
import com.google.gson.annotations.Until;

public class MemberInfo {

	private int id;

	private String name;

	@Until(1.7)
	private String yahooAccount;

	@Since(2.3)
	private String facebookAccount;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public String getYahooAccount() {
		return yahooAccount;
	}

	public void setYahooAccount(String yahooAccount) {
		this.yahooAccount = yahooAccount;
	}

	public String getFacebookAccount() {
		return facebookAccount;
	}

	public void setFacebookAccount(String facebookAccount) {
		this.facebookAccount = facebookAccount;
	}

}

Trong lớp trên, một lớp MemberInfo có 4 field: id, name, yahooAccount và facebookAccount. Trong đó, field yahooAccount được xóa bỏ từ phiên bản 1.7 và field facebookAccount được thêm từ phiên bản 2.3.

Tương tự như @Expose, để chú thích này có hiệu lực chúng ta phải thực hiện các cấu hình như sau:


package com.gpcoder.gson.annotation;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.gpcoder.gson.object.MemberInfo;

public class VersioningExample {
	public static void main(final String[] args) throws Exception {

		final GsonBuilder builder = new GsonBuilder();
		builder.setVersion(2.0);

		final Gson gson = builder.create();

		final MemberInfo member = new MemberInfo();
		member.setId(1);
		member.setName("GP Coder");
		member.setYahooAccount("gpcoder@yahoo.com"); // Until version 1.7
		member.setFacebookAccount("facebook.com/GPCoder/"); // Since version 2.0

		final String json = gson.toJson(member);
		System.out.println("Json: " + json);

		// Parse JSON to Java
		String json2 = "{\"id\":1,\"name\":\"GP Coder\",\"yahooAccount\":\"gpcoder@yahoo.com\",\"facebookAccount\":\"facebook.com/GPCoder/\"}";
		final MemberInfo otherMember = gson.fromJson(json2, MemberInfo.class);
		System.out.println("Deserialised (version 1.0)");
		System.out.printf("  + Id:              %s%n", otherMember.getId());
		System.out.printf("  + Name:            %s%n", otherMember.getName());
		System.out.printf("  + YahooAccount:    %s Until(1.7)%n", otherMember.getYahooAccount());
		System.out.printf("  + FacebookAccount: %s Since(2.3)%n", otherMember.getFacebookAccount());
	}
}

Kết quả thực thi chương trình trên:


Json: {"id":1,"name":"GP Coder"}
Deserialised (version 1.0)
  + Id:              1
  + Name:            GP Coder
  + YahooAccount:    null Until(1.7)
  + FacebookAccount: null Since(2.3)

Trong ví dụ này, tôi đã xác định phiên bản là 2.0, có nghĩa là chỉ có Id và Name sẽ được xử lý. FacebookAccount được thêm vào phiên bản 2.3, do đó không thõa điều kiện và YahooAccount đã bị xóa sau phiên bản 1.7 nên cũng không thõa điều kiện.

Áp dụng tùy chỉnh TypeAdapter, JsonSerializer, JsonDeserializer với @JsonAdapter

Chú thích này có thể được sử dụng field hoặc class để chỉ định lớp tùy chỉnh Gson TypeAdapter, JsonSerializer, JsonDeserializer được sử dụng trong khi serialization/ deserialization. Các lớp này sẽ được giới thiệu ở các bài viết tiếp theo.

Dưới đây là một adapter tùy chỉnh mở rộng từ lớp TypeAdapter và ghi đè lên các phương thức read() và write(). Các phương thức này là được sử dụng trong quá trình serialization/ deserialization của lớp / trường được cấu hình để sử dụng bộ điều hợp Loại tùy chỉnh này.

Các phương thức read() và write() này dựa trên cách tiếp cận dựa trên token. Trong quá trình deserialization / read, chúng ta đọc từng token riêng lẻ sử dụng phương thức hasNext và nextName của JsonReader, kiểm tra kiểu của chúng và nhận giá trị dựa trên kiểu sử dụng các phương thức getXXX. Tương tự, trong quá trình Serialization / write, chúng ta sử dụng các phương thức giá trị name và value của JsonWriter. Cần lưu cần xác định chính xác các phương thức beginObject/ endObject và beginArray/ endArray tương ứng với chuỗi json Object và Array.

Hãy xem ví dụ sau:


package com.gpcoder.gson.object;

import java.util.Arrays;
import java.util.Date;

import com.google.gson.annotations.JsonAdapter;
import com.gpcoder.gson.annotation.CustomTypeAdapter;

public class AmazonBook {
	private String title;
	private String[] authors;
	private String isbn10;
	private String isbn13;
	private Double price;
	private Date publishedDate;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String[] getAuthors() {
		return authors;
	}

	public void setAuthors(String[] authors) {
		this.authors = authors;
	}

	public String getIsbn10() {
		return isbn10;
	}

	public void setIsbn10(String isbn10) {
		this.isbn10 = isbn10;
	}

	public String getIsbn13() {
		return isbn13;
	}

	public void setIsbn13(String isbn13) {
		this.isbn13 = isbn13;
	}

	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}

	public Date getPublishedDate() {
		return publishedDate;
	}

	public void setPublishedDate(Date publishedDate) {
		this.publishedDate = publishedDate;
	}

	@Override
	public String toString() {
		return "Book [title=" + title + ", authors=" + Arrays.toString(authors) + ", isbn10=" + isbn10 + ", isbn13="
				+ isbn13 + ", price=" + price + ", publishedDate=" + publishedDate + "]";
	}

}

Tạo một lớp custom TypeAdapter như sau:


package com.gpcoder.gson.annotation;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.gpcoder.gson.object.AmazonBook;

public class CustomTypeAdapter extends TypeAdapter<AmazonBook> {

	public static final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

	@Override
	public AmazonBook read(final JsonReader in) throws IOException {
		final AmazonBook book = new AmazonBook();

		in.beginObject();
		while (in.hasNext()) {
			switch (in.nextName()) {
			case "title":
				book.setTitle(in.nextString());
				break;
			case "isbn-10":
				book.setIsbn10(in.nextString());
				break;
			case "isbn-13":
				book.setIsbn13(in.nextString());
				break;
			case "price":
				book.setPrice(in.nextDouble());
				break;
			case "publishedDate":
				Date publishedDate = null;
				try {
					publishedDate = sdf.parse(in.nextString());
				} catch (ParseException e) {
					e.printStackTrace();
				}
				book.setPublishedDate(publishedDate);
				break;
			case "authors":
				in.beginArray();
				final List<String> authors = new ArrayList<>();
				while (in.hasNext()) {
					authors.add(in.nextString());
				}
				in.endArray();
				book.setAuthors(authors.toArray(new String[authors.size()]));
				break;
			}
		}
		in.endObject();

		return book;
	}

	@Override
	public void write(final JsonWriter out, final AmazonBook book) throws IOException {
		out.beginObject();
		out.name("title").value(book.getTitle());
		out.name("isbn-10").value(book.getIsbn10());
		out.name("isbn-13").value(book.getIsbn13());
		out.name("price").value(book.getPrice());
		out.name("publishedDate").value(sdf.format(book.getPublishedDate()));
		out.name("authors");
		out.beginArray();
		for (final String author : book.getAuthors()) {
			out.value(author);
		}
		out.endArray();
		out.endObject();
	}
}

Bây giờ hãy thêm @JsonAdapter vào lớp AmazonBook để xác định sử dụng lớp custom Adapter trên:


@JsonAdapter(value = CustomTypeAdapter.class)
public class AmazonBook {
    // Fields ...
}

Chương trình sử dụng JsonAdaper:


package com.gpcoder.gson.annotation;

import java.util.Calendar;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.gpcoder.gson.object.AmazonBook;

public class JsonAdapterExample {
	public static void main(String args[]) {
		final GsonBuilder gsonBuilder = new GsonBuilder();
		gsonBuilder.setPrettyPrinting();

		final Gson gson = gsonBuilder.create();

		final AmazonBook book = new AmazonBook();
		book.setTitle("Head First Design Patterns");
		book.setIsbn10("0596007124");
		book.setIsbn13("978-0596007126");
		book.setPrice(52.41);

		Calendar c = Calendar.getInstance();
		c.set(2004, Calendar.OCTOBER, 1);
		book.setPublishedDate(c.getTime());

		String[] authors = new String[] { "Eric Freeman", "Bert Bates", "Kathy Sierra", "Elisabeth Robson" };
		book.setAuthors(authors);

		System.out.println("Convert Book object to JSON string: ");
		final String json = gson.toJson(book);
		System.out.println(json);

		System.out.println("Convert JSON String to Book object: ");
		final AmazonBook parsedBook1 = gson.fromJson(json, AmazonBook.class);
		System.out.println(parsedBook1);
	}
}

Kết quả thực thi chương trình trên:


Convert Book object to JSON string: 
{
  "title": "Head First Design Patterns",
  "isbn-10": "0596007124",
  "isbn-13": "978-0596007126",
  "price": 52.41,
  "publishedDate": "01/10/2004",
  "authors": [
    "Eric Freeman",
    "Bert Bates",
    "Kathy Sierra",
    "Elisabeth Robson"
  ]
}

Convert JSON String to Book object: 
Book [title=Head First Design Patterns, authors=[Eric Freeman, Bert Bates, Kathy Sierra, Elisabeth Robson], isbn10=0596007124, isbn13=978-0596007126, price=52.41, publishedDate=Fri Oct 01 00:00:00 ICT 2004]

Ngoài ra, bạn có thể chọn không sử dụng chú thích này @JsonAdapter và chỉ cần đăng ký custom TypeAdapter ở trên với GsonBuilder như dưới đây:


final GsonBuilder gsonBuilder = new GsonBuilder();
		gsonBuilder.registerTypeAdapter(AmazonBook.class, new CustomTypeAdapter());
		gsonBuilder.setPrettyPrinting();

Kết quả cũng tương tự như việc sử dụng Annotation @JsonAdapter. Chi tiết về custom TypeAdapter, hãy xem bài viết Hướng dẫn sử dụng Gson TypeAdapter.

Trên đây là những ví dụ cơ bản về sử dụng các Annotation (chú thích) của Gson. Các chú thích này cung cấp rất nhiều tùy chỉnh mà không cần phải viết các lớp phức tạp. Tuy nhiên, các chú thích có những hạn chế và không thể xử lý tất cả các trường hợp như chuyển đổi giữa các cấu trúc khác nhau hoặc cần xử lý cache để tăng hiệu suất. Khi trường hợp cụ thể phát sinh, chúng ta nên sử dụng tùy chỉnh JsonDeserializer<T> và một JsonSerializer<T>. Phần này sẽ được giới thiệu ở bài viết tiếp theo.

5.0
03
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Json Được gắn thẻ: Annotation, Gson, json

Hướng dẫn Gson Streaming API để đọc và ghi JSON
Hướng dẫn sử dụng Gson ExclusionStrategy

Có thể bạn muốn xem:

  • Hướng dẫn sử dụng Gson ExclusionStrategy (13/01/2018)
  • Hướng dẫn sử dụng Gson TypeAdapter (18/01/2018)
  • Giới thiệu Json (05/01/2018)
  • Hướng dẫn sử dụng thư viện Jackson (21/01/2018)
  • Hướng dẫn Gson Streaming API để đọc và ghi JSON (08/01/2018)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (98061 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97702 lượt xem)
  • Giới thiệu Design Patterns (87770 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (86441 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83840 lượt xem)

Nội dung bài viết

  • 1 Tùy chỉnh tên field sử dụng @SerializedName
  • 2 Loại trừ field sử dụng @Expose
  • 3 Quản lý phiên bản với @Since và @Until
  • 4 Áp dụng tùy chỉnh TypeAdapter, JsonSerializer, JsonDeserializer với @JsonAdapter

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link