我是庖丁,<肢解IOT平台>之物模型-LMLPHP

前言

物模型是对设备在云端的功能描述,包括设备的属性,数据,服务和事件。

物联网平台通过定义一种物的描述语言来描述物模型,称之为 TSL(即 Thing Specification Language),采用JSON格式,您可以根据TSL组装上报设备的数据。

最终能达到的效果:

  • 识别JSON中的键值内容,默认情况下,Key始终是一个字符串,而value可以是String,boolean,double或long。

  • 解析识别JSON字符串和JSON数组类型的字符串

  • 解析识别带有毫秒精度的unix时间戳的JSON字符串

效果如下:

引入依赖

使用序列化框架GSON对JSON格式的键值对进行识别解析,可以通过引入com.google.code.gson来配置关系。

 <dependency>
           <groupId>com.google.code.gson</groupId>
           <artifactId>gson</artifactId>
</dependency>

键值属性

我是庖丁,&lt;肢解IOT平台&gt;之物模型-LMLPHP

KvEntry

KvEntry中提供了获取键值对属性的基本接口,例如获取字符属性的键,值和获取字符串,布尔型和数字类型的接口方法。BasicKvEntry定义了键只能为字符串类型,LongDataEntry,BooleanDataEntry,DoubleDataEntry和StringDataEntry分别定义了相应属性的值。


public interface KvEntry extends Serializable {

   String getKey();

   DataType getDataType();

   Optional<String> getStrValue();

   Optional<Long> getLongValue();

   Optional<Boolean> getBooleanValue();

   Optional<Double> getDoubleValue();

   String getValueAsString();

   Object getValue();

}

我是庖丁,&lt;肢解IOT平台&gt;之物模型-LMLPHP

属性和上传数据

通过将来自设备的消息根据类型划分为设备属性(AttributesUpdateRequest)和设备上传数据(TelemetryUploadRequest),

其中TelemetryUploadRequest包含了Long型的unix时间戳。

Json识别解析

属性识别解析

UML 时序图如下:

我是庖丁,&lt;肢解IOT平台&gt;之物模型-LMLPHP

public class JsonConverter {

  private static final Gson GSON = new Gson();
  public static final String CAN_T_PARSE_VALUE = "Can't parse value: ";

  //遍历键值属性,对相应键值进行处理
  public static List<KvEntry> parseValues(JsonObject valuesObject) {
      List<KvEntry> result = new ArrayList<>();
      for (Map.Entry<String, JsonElement> valueEntry : valuesObject.entrySet()) {
          JsonElement element = valueEntry.getValue();
          if (element.isJsonPrimitive()) {
              JsonPrimitive value = element.getAsJsonPrimitive();
              //如果值为字符串
              if (value.isString()) {
              //新建StringDataEntry
                  result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString()));
              //如果值为布尔型
              } else if (value.isBoolean()) {
              //新建BooleanDataEntry
                  result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean()));
                  //如果值为数值类型
              } else if (value.isNumber()) {
                  parseNumericValue(result, valueEntry, value);
              } else {
                  throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
              }
          } else {
              throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
          }
      }
      return result;
  }
   
  private static void parseNumericValue(List<KvEntry> result, Map.Entry<String, JsonElement> valueEntry, JsonPrimitive value) {
  //数值转化为字符串类型,并判断是不是包含".",来判断是Long,还是Double
      if (value.getAsString().contains(".")) {
          result.add(new DoubleDataEntry(valueEntry.getKey(), value.getAsDouble()));
      } else {
          try {
              long longValue = Long.parseLong(value.getAsString());
              result.add(new LongDataEntry(valueEntry.getKey(), longValue));
          } catch (NumberFormatException e) {
              throw new JsonSyntaxException("Big integer values are not supported!");
          }
      }
  }

  public static AttributesUpdateRequest convertToAttributes(JsonElement element) {
      return convertToAttributes(element, BasicRequest.DEFAULT_REQUEST_ID);
  }

  public static AttributesUpdateRequest convertToAttributes(JsonElement element, int requestId) {
      if (element.isJsonObject()) {
          BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId);
          long ts = System.currentTimeMillis();
          //将JSON字符串解析为键值属性的集合
          request.add(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList()));
          return request;
      } else {
          throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
      }
  }
}

运行

05-04 05:19