背景
在创建聊天应用的基础上,我写了一篇关于服务器和浏览器之间的WebSocket通信的文章。用 Java 实现服务器,用 javascript 实现客户端。
[过去关于套接字通信的文章] ・使用 Java 和 JavaScript与 Web 浏览器进行套接字通信① ・使用 Java 和 JavaScript 与 Web 浏览器进行套接字通信②
这一次,我们将在修改聊天应用程序的同时学习“如何处理 JSON 数据”。您还将了解“通过套接字通信发送和接收 JSON 数据”。
目的
- 了解如何在 Java 和 JavaScript 中处理 JSON。
- 了解如何在 WebSocket 通信中以 JSON 格式发送和接收数据。
- 使用 JSON 实现 WebSocket 通信的多路径。
前提
在本文中,我将主要描述如何处理 JSON。有关套接字通信的说明,请参阅过去的文章和 google 页面。⇒ 用 Java 实现服务器程序,用 JavaScript 实现客户端程序。
什么是 JSON
http://www.tohoho-web.com/ex/json.html 示例:{" name ":" Tanaka "," age ": 26}
以关联数组的形式保存值的事物。简而言之,如果你把它想象成一个**只是一个字符串**,就很容易理解了。(虽然不是真的)细节被省略。
实用内容
- 使用 JavaScript 处理 JSON 数据(编码、解码)
- 在 Java 中处理 JSON 数据(编码、解码)
- 如何在socket通信中发送和接收JSON数据
- 使用 JSON 传输/接收创建多通道聊天应用程序
1. 用 JavaScript 处理 JSON 数据
JSON 数据可以在 JavaScript 中轻松处理。
编码
对象⇒ JSON 使用JSON.stringify ()
方法。
Example of use
var obj = {
name: 'Taro',
age: 30,
area: 'Tokyo'
}
var json = JSON.stringify(obj);
console.log(json);
Execution result
{"name":"Taro Tanaka","age":30}
- 以关联数组的形式为变量“obj”赋值。(创建对象)
- 使用编码
JSON.stringify (obj)
并将其分配给变量json
。(转换为 JSON) - 使用 .在控制台上显示 JSON 数据
console.log (json)
。
查看执行结果,可以看到关联数组格式的对象已经转换为 JSON 格式。⇒键和字符串用“”“括起来。数字保持裸露。
解码
JSON ⇒ 对象 使用JSON.parse ()
方法。
Example of use
var obj1 = {
name: 'Taro',
age: 30,
area: 'Tokyo'
}
var json = JSON.stringify(obj1);
//-----JSON data preparation so far-----
var obj2 = JSON.parse(json);
console.log(obj2);
console.log(obj2.name);
Execution result
{name: "Taro", age: 30, area: "Tokyo"}
Taro
- 以关联数组的形式为变量“obj1”赋值。(创建对象)
- 使用编码
JSON.stringify (obj1)
并将其分配给变量json
。(转换为 JSON) ** ---------- 到目前为止 JSON 数据准备(与上一节中的编码相同) ---------- ** - 使用解码
JSON.parse (json)
并将其分配给变量ʻobj2`。(转换为对象) - 使用 显示控制台上的对象
console.log (obj2)
。 - 您还可以使用 `obj2 访问每个属性。~`。
查看执行结果,可以看到 JSON 数据已经转换为对象。`obj1 (object) ⇒
json (JSON) ⇒ ʻobj2
(对象)
2.用Java处理JSON数据
Java 对象指的是类,不像 JavaScript 那样容易创建。在转换 JSON 和对象时,需要提前创建一个类**,该类具有与 JSON 变量对应的属性。
如果您在 Java 中处理 JSON,建议使用外部库。⇒ 处理 JSON 的 Java 标准 API 也是可用的,但需要大量时间和精力。
以下是处理 JSON 的著名外部库。
- 杰克逊
- GSON
- JSONIC --Java 中的 JSON 等。
基本用法类似,但这次我将使用“杰克逊”。有关如何使用其他库以及如何应用外部库的信息,请酌情查看参考页面或 google。
编码
Object ⇒ JSON 使用writeValueAsString ()
ʻObjectMapper` 类的方法进行编码。
how to use
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(Object instance);
Example of use
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
//A class with properties to convert to JSON
class Info {
public String name = "Taro Tanaka";
public int age = 30;
}
//Class to perform encoding
public class Main {
public static void main(String[] args) {
Info info = new Info();//Instantiate a class to convert to JSON
ObjectMapper mapper = new ObjectMapper();//Create an instance of the ObjectMapper class
try {
//writeValueAsString()Encoding by method
String script = mapper.writeValueAsString(info);
System.out.println(script);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Execution result
{"name":"Taro Tanaka","age":30}
- 在 ʻInfo` 类中创建要转换为 JSON 的属性。
- 创建一个
Main
类来执行编码。 - 使用ʻInfo info = new Info ()` 实例化要转换为 JSON 的对象。
- 实例化`
class with
ObjectMapper ObjectMapper mapper = new ObjectMapper()`。 - 使用 .将对象转换为 JSON
mapper.writeValueAsString (info)
。这时候就需要用try和catch来处理错误了。 - 使用 .将 JSON 数据输出到控制台
System.out.println (script)
。
查看执行结果,可以看到对象类已经转换为 JSON 格式。每个属性的变量名称和值存储为一对。
解码
JSON ⇒ 对象 使用readValue ()
ʻObjectMapper` 类的方法进行解码。
how to use
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(JSON data,Object class.class);
Example of use
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
//A class with properties that are converted from JSON
class Info {
public String name;
public int age;
}
//Class to perform decoding
public class Main {
public static void main(String[] args) {
String script = "{ \"name\":\"Taro Tanaka\", \"age\":30}";//Create JSON data as a string
ObjectMapper mapper = new ObjectMapper();//Create an instance of the ObjectMapper class
try {
//readValue()Decoding by method
Info info = mapper.readValue(script, Info.class);
System.out.println(info.name);
System.out.println(info.age);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Execution result
Taro Tanaka
30
- 在 ʻInfo` 类中创建一个具有从 JSON 转换而来的属性的类。
- 创建一个
Main
类来执行解码。 - 这里,JSON数据被准备为String字符串。
- 实例化`
class with
ObjectMapper ObjectMapper mapper = new ObjectMapper()`。 - 使用 .从 JSON 转换为对象
mapper.readValue (script, Info.class)
。这时候就需要用try和catch来处理错误了。 - 使用 .将对象属性输出到控制台
System.out.println (info. ~)
。
查看执行结果,可以看到 JSON 数据已经转换为对象类。解码后,可以通过访问指定对象的每个属性来获取值。
3.socket通信中如何收发JSON数据
服务器程序:Java 客户端程序:JavaScript
描述进行socket通信时用JSON发送和接收的方法。由于 Java 和 JavaScript 的处理方式不同,我们将分别进行解释。
使用 JavaScript 发送和接收 JSON
JavaScript 没有什么特别之处。如上所述,您可以通过JSON.stringify ()
方法和JSON.parse ()
方法轻松地在对象和 JSON 之间进行转换。如果客户端在发送前编码,接收后解码,则没有问题。
传输时的编码
对象⇒ JSON
When sending
var obj = { type:'A' , msg:'a' };
var json = JSON.stringify(obj);
socket.send(json);
- 准备对象。
-
JSON.stringify ()
使用该方法编码为 JSON 。 -
send ()
使用WebSocket的方法发送JSON数据
接收时解码
JSON ⇒ 对象
When receiving
socket.onmessage = function(e) {
var obj = JSON.parse(e.data);
};
- 接收带有 ʻon message` 的 JSON 数据。
- 解码以反对
JSON.parse (e.data)
在 Java 中发送和接收 JSON
当使用 JSON 进行 Java 套接字通信时,它并不像 JavaScript 那样简单。⇒ 需要准备编码器、解码器和对象类。
传输时的编码
对象⇒ JSON
通过套接字通信发送 JSON 数据时,请在发送前使用编码器将对象转换为 JSON。
通常sendText ()
发送文本数据时使用该sendObject ()
方法,但发送JSON时使用该方法。参数是一个对象,由编码器转换为 JSON,然后发送。
- 创建要发送的对象。
- 创建编码器。
- 在 @ServerEndpoint 注解中注册编码器。
1. 创建一个要发送的对象
创建一个具有像普通类一样的属性的类。像往常一样描述构造函数、setter 和 getter。(没有它也可以转换。)
Object class
public class JsonObj {
private String type = "type1";
private String msg = "msg1";
//constructor
public JsonObj() {}
//Setter
public void setType(String type) {this.type = type;}
public void setMsg(String msg) {this.msg = msg;}
//Getter
public String getType() {return type;}
public String getMsg() {return msg;}
}
2. 创建编码器
编码器实现了`Encoder.Text class in the
javax.websocket package. (There is also the ʻEncoder.Binary
类,但是因为是处理二进制数据的类所以省略)
ʻEncoder.Text <> `generics 描述了要编码的对象类。
encoder
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonEncoder implements Encoder.Text<JsonObj>{
@Override//Initialization does nothing
public void init(EndpointConfig config) {}
@Override//Encoding process(Object → JSON)
public String encode(JsonObj obj) throws EncodeException {
ObjectMapper mapper = new ObjectMapper();
String json = "";
try {
json = mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return json;
}
@Override//Do nothing to destroy
public void destroy() {}
}
`如果你继承了Encoder.Text`类,你需要重写以下方法。
- `init(EndpointConfig config)`:编码器启动时的处理。
- ʻencode(Object obj)`:编码过程。
-
destroy ()
: 编码器被破坏时的处理。
基本上 ʻinit() and
destroy() do not have to do anything. In ʻencode (Object)
,将 ** 参数设置为 object ** 并指定 JSON ** 作为编码后的返回值。
3.注册编码器
@ServerEndpoint
//Specify encoder class
@ServerEndpoint(value = "/json" , encoders = JsonEncoder.class)
public class JsonTest {
//Omission
//sendObject()The argument of is an object
//Encoded before sending
session.getAsyncRemote().sendObject(obj)
//Omission
}
使用编码器时,请在 中指定编码器类@ServerEndpoint ()
。通过此处指定,将由发送对象时指定的编码器转换为 JSON 数据。
-- sendObject (obj)
** ⇒ 编码过程 (obj → JSON) ⇒ 发送 **
接收时解码
JSON ⇒ 对象
通过socket通信接收JSON数据时,在接收前使用解码器将JSON转换为对象。
通常,作为`onMessage()`方法的参数作为String类型的字符串接收,但通过使用解码器,在接收之前进行解码处理,并作为对象接收。
- 创建要接收的 JSON 转换目标对象。
- 创建解码器。
- 在 @ServerEndpoint 注释中注册解码器。
1.创建目标对象
解码时,需要准备一个对象类,该对象类具有对应于 JSON 元素的属性。如果接收多个 JSON 时内容和格式不同,则必须为每个 JSON 准备多个对象类。
在编码的情况下,所有对象都可以转换成JSON随时发送,在解码的情况下,需要提前掌握接收到的JSON的内容,并在解码前准备对象类作为接收端口。
Object class
public class JsonObj {
private String type;
private String msg;
//constructor
public JsonObj() {}
//Setter
public void setType(String type) {this.type = type;}
public void setMsg(String msg) {this.msg = msg;}
//Getter
public String getType() {return type;}
public String getMsg() {return msg;}
}
2.创建解码器
解码器实现包Decoder.Text
中的类 javax.websocket
。(还有一个Decoder.Binary
类,但是因为是处理二进制数据的类所以省略了。)
在 的泛型中描述解码目的地的对象类Decoder.Text <>
。
Decoder
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonDecoder implements Decoder.Text<JsonObj> {
@Override//Initialization does nothing
public void init(EndpointConfig config) {}
@Override//Judgment of whether decoding is possible
public boolean willDecode(String text) {
return (text != null);
}
@Override//Decoding process(JSON → object)
public JsonObj decode(String text) throws DecodeException {
ObjectMapper mapper = new ObjectMapper();
JsonObj obj = null;
try {
obj = mapper.readValue(text, JsonObj.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return obj;
}
@Override//Do nothing to destroy
public void destroy() {}
}
如果继承Decoder.Text
该类,则需要重写以下方法。
- ʻinit(EndpointConfig config)`:解码器启动时的处理。
-
willDecode (String text)
:判断是否执行解码过程。 -
decode (Object obj)
: 解码过程。 -
destroy ()
: 解码器被破坏时的处理。
基本上ʻinit() and
destroy() do not have to do anything. Set the ** argument of
willDecode() to JSON **, and if the return value is
true , execute the following decoding. If it is
false , it will not be decoded and the subsequent
@OnMessage methods will not be executed. In
decode()`,将**参数设置为JSON **并指定object **作为编码后的返回值。
3.注册解码器
@ServerEndpoint
//Specify decoder class
@ServerEndpoint(value = "/json" , decoders = JsonDecoder.class)
public class JsonTest {
//Omission
//@Decoded before the OnMessage method
@OnMessage
public void onMessage(JsonObj obj , Session mySession) {
}
//Omission
}
使用解码器时,请在 中指定解码器类@ServerEndpoint ()
。通过此处指定,将由接收数据时指定的解码器转换为对象。⇒@OnMessage
方法的参数是**对象类型**。(通常是字符串类型)
-** 接收 ⇒ 解码过程(JSON → obj) ⇒ ** @OnMessage
** 方法 **
4.创建一个多通道聊天应用程序
修改Past中创建的聊天应用。
变化点
- 将要发送和接收的数据从文本更改为 JSON(使用 JSON)
- 将聊天字段从 1 增加到 2(实现多次通行证)
在 WebSocket 通信中,只有一个套接字用于接收数据。也就是说,因为是一次性的,所以即使有多个来源也无法区分。如果你能分辨,你必须分解字符串的内容来区分它们。无论如何要解码,处理JSON很方便。
其实接收方式有3种:1.Text 2.Binary 3.PingPong,但是由于我们这里只处理文本格式,所以我们认为只有一个socket。
创建文件
- JsonIndex.html:用于浏览器显示的 HTML 文件
- JsonSocket.js:Socket通信客户端程序
- JsonTest.java:Socket通信服务器程序
- JsonObj.java:与 JSON 相互转换的对象类
- JsonEncoder.java:Java 编码器
- JsonDecoder.java:Java 解码器
1. 用于显示的 HTML
JsonIndex.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Send / receive JSON</title>
<script type="text/javascript" src="JsonSocket.js"></script>
</head>
<body>
<div
style="width: 500px; height: 200px; overflow-y: auto; border: 1px solid #333;"
id="show1"></div>
<input type="text" size="80" id="msg1" name="msg1" />
<input type="button" value="Send" onclick="sendMsg1();" />
<p></p>
<div
style="width: 500px; height: 200px; overflow-y: auto; border: 1px solid #333;"
id="show2"></div>
<input type="text" size="80" id="msg2" name="msg2" />
<input type="button" value="Send" onclick="sendMsg2();" />
</body>
</html>
观点
- 将聊天字段增加到两个。显示框分别为
show1
和show2
,文本框分别为msg1
和msg2
。使用稍后描述的 JavaScript 文件操作这些。
2.客户端程序
JsonSocket.js
//Object creation for JSON
var obj = { type:null , msg:null };
//WebSocket object generation
var wSck= new WebSocket("ws://localhost:8080/jsonTest/json");
//Action when connecting socket
wSck.onopen = function() {
document.getElementById('show1').innerHTML += "Connected." + "<br/>";
document.getElementById('show2').innerHTML += "I'm connected ~" + "<br/>";
};
//Action when receiving a message
wSck.onmessage = function(e) {
//Decode JSON data into an object
var json = JSON.parse(e.data);
//Change the execution content depending on the type value of JSON data
if(json.type === 'msg1'){document.getElementById('show1').innerHTML += json.msg + "<br/>";}
else if(json.type === 'msg2'){document.getElementById('show2').innerHTML += json.msg + "<br/>";}
};
//Send message 1
var sendMsg1 = function(val) {
var element = document.getElementById('msg1')
obj.type = element.name;//Substitute the contents of the object
obj.msg = element.value;
var json = JSON.stringify(obj);//Encode object to JSON
wSck.send(json);//Send JSON
element.value = "";//Clear the contents
};
//Send message 2
var sendMsg2 = function(val) {
var element = document.getElementById('msg2');
obj.type = element.name;
obj.msg = element.value;
var json = JSON.stringify(obj);
wSck.send(json);
element.value = "";
};
观点
- 为 JSON 创建一个对象 ʻobj` ⇒ 这被转换为 JSON。
- 随着聊天字段的增加,在连接套接字时添加了操作。
- 收到消息后,将JSON解码为对象,根据type值改变处理内容。
- 消息传输1、2:给`obj`赋值后,转换成JSON再发送给服务器。
3.服务器程序
JsonTest.java
package jsonTest;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
//Describe the decoder and encoder in the arguments
@ServerEndpoint(value = "/json" , decoders = JsonDecoder.class , encoders = JsonEncoder.class)
public class JsonTest {
private static Set<Session> user = new CopyOnWriteArraySet<>();
@OnOpen
public void onOpen(Session mySession) {
System.out.println("connect ID:"+mySession.getId());
user.add(mySession);
}
//Decoded before this method
@OnMessage
public void onMessage(JsonObj obj , Session mySession) {
for (Session user : user) {
user.getAsyncRemote().sendObject(obj);//What you send is an object(Encoded before sending)
System.out.println(user.getId()+"Second"+mySession.getId()+"I sent the second message!");
}
if(obj.getMsg().equals("bye")) {onClose(mySession);}
}
@OnClose
public void onClose(Session mySession) {
System.out.println("disconnect ID:"+mySession.getId());
user.remove(mySession);
try {
mySession.close();
} catch (IOException e) {
System.err.println("An error has occurred: " + e);
}
}
}
观点
- 在 中指定解码器和编码器类
@ServerEndpoint
。 - 当从客户端接收到数据时,会在方法执行之前执行指定的解码器,
@OnMesaage
并将 JSON 转换为对象。⇒@OnMesaage
方法的参数是ʻObj` 类型。 - 使用该
sendObject (obj)
方法发送对象时,会在发送到客户端之前执行指定的编码器,并将对象转换为 JSON。
-
sendText ()
发送字符串时使用,sendObject ()
发送对象时使用。
4.对象类
JsonObj.java
package jsonTest;
public class JsonObj {
private String type;
private String msg;
//constructor
public JsonObj() {}
//Setter
public void setType(String type) {this.type = type;}
public void setMsg(String msg) {this.msg = msg;}
//Getter
public String getType() {return type;}
public String getMsg() {return msg;}
}
观点
- 这个类有两个属性,
type
和msg
。⇒ 创建一个对应于客户端程序对象(接收到的 JSON)的类。 - 解码后,通过
type
属性值判断来源。
5.编码器
JsonEncoder.java
package jsonTest;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonEncoder implements Encoder.Text<JsonObj>{
@Override//Initialization does nothing
public void init(EndpointConfig config) {}
@Override//Encoding process(Object → JSON)
public String encode(JsonObj obj) throws EncodeException {
ObjectMapper mapper = new ObjectMapper();
String json = "";
try {
json = mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return json;
}
@Override//Do nothing to destroy
public void destroy() {}
}
观点
- 创建一个实现`Encoder.Text的类`。
- ʻencode()`方法的参数是一个对象,返回值是JSON。
- 使用
writeValueAsString ()
ʻObjectMapper` 类的方法进行编码。
6.解码器
package jsonTest;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonDecoder implements Decoder.Text<JsonObj> {
@Override//Initialization does nothing
public void init(EndpointConfig config) {}
@Override//Judgment of whether decoding is possible
public boolean willDecode(String text) {
return (text != null);
}
@Override//Decoding process(JSON → object)
public JsonObj decode(String text) throws DecodeException {
ObjectMapper mapper = new ObjectMapper();
JsonObj obj = null;
try {
obj = mapper.readValue(text, JsonObj.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return obj;
}
@Override//Do nothing to destroy
public void destroy() {}
}
观点
- 创建一个实现
Decoder.Text <JsonObj>
. - 该
decode ()
方法的参数是一个字符串(JSON),返回值是一个对象。 - 使用
readValue ()
ʻObjectMapper` 类的方法进行编码。
执行结果
JsonIndex.html
在 Chrome 中 执行。
聊天字段已增加到两个。您可以在每个聊天字段中独立发送和接收。⇒ 多路径的实现完成。
执行顺序
现在它很长,描述客户端和服务器上发生的事情。
- 以 HTML 格式显示页面。
- 握手完成后,套接字通信开始。
- 在文本框中写一条消息,然后单击发送按钮。
- 使用 JavaScript 将对象编码为 JSON 并将其发送到服务器。
- 在服务器上接收 JSON 数据。
- 使用解码器从 JSON 解码为对象。
-
@OnMessage
以解码的对象作为参数执行该方法。 - 使用方法发送对象
sendObject ()
。 - 在发送之前将对象编码为 JSON 并发送给客户端。
- 在客户端接收数据。
- 将 JSON 数据解码为对象。
- 通过
type
属性值确定来源并向每个来源显示一条消息。
其他
改进点
-如果接收到未知的JSON数据,会因为没有准备好对应的对象类而出错。⇒有人支持它,所以我可能会尽快检查它。它看起来相当复杂。
- 接收多种 JSON 数据时不支持。⇒ 好像没那么难。可以通过增加解码器和对象类来支持JSON的内容。
- 不知道应该创建什么样的对象类来支持嵌套的 JSON。⇒ 如果你google,它会出来,如果有必要,检查一下。
- 由于对二进制数据的处理不是很了解,所以不支持二进制数据的发送和接收。⇒必要时检查,例如发送和接收图像。
展示次数
它比我预期的要复杂。我后悔了,因为它变长了。我应该总结得更多。
在Java中处理JSON,使用外部库和准备一个对象类是很麻烦的。使用 JavaScript,它以一行结尾。
Java socket通信中处理JSON时,有编码类和解码类方便吗?必须准备一个编码器类和一个解码器类是很麻烦的。
这很麻烦,但如果你想使用 JSON,你就无能为力了。对如何在 Java 中处理 JSON 有更深入的了解是件好事。看来您可以在个人生产级别使用它。