DataSet - 多态集合






4.70/5 (12投票s)
一组提供多态数据结构的类。
介绍
DataSet
是一个简单的类,可以保存任何类型的项目。我最初使用它为客户端层提供数据,仅通过公开数据来封装服务器端的功能。然后,它扩展了 ConfigData
,这是一个包装的 DataSet
,提供配置数据并强制读取所有项目。
这篇文章描述了我用来支持 DataSet
的类包。单独来看,这没什么,但我意识到我想要发表文章的很多其他主题都需要它,因为它是我传递数据的实际结构。
这可以用来代替 JSON,我打算很快让转换器可靠地工作。
背景
通常情况下,一个类没有任何功能,只有值。随着时间的推移,我厌倦了编写无数只有访问方法的类。因此,基于我曾在各种工作中使用的设计,我开始构建自己的类。
基类
结构非常简单。一个 DataSet
包含一组 DataItem
对象。每个项目都采用一个名称/值对,并且一旦创建就不可变。由于在使用泛型时遇到了麻烦,我改变了设计,使该项目保存一个 Object
,而类本身确保了安全的类型转换,而不是必须在更高级别处理它。
在 DataSet
中,项目可以作为对象检索,也可以直接访问值;这省去了很多空值检查和方法链。
可以安全地从 DataSet
和 DataItem
调用的“原生”类型都列在枚举 ValueType
中。所有其他类型都被视为 Object
,消费者有责任安全地管理它们。
然后有两个包装类。ConfigData
用于从文件加载应用程序的配置,而 SealedDataSet
提供对 DataSet
的只读访问。
传输层
我使用 DataSet
的两个主要用途是通信和持久化。为此,我提供了两个读写数据的类。
DataReader
用于从 BufferedReader
中获取传入的 String
并将其转换为 DataSet
。反之,DataWriter
将 DataSet
转换为字符串并将其写入 BufferedWritter
。这些字符串的格式简洁明了
从 JavaDoc 中提取的格式示例是
# comments can be added at any point and each
# item is written thus:
# name t value
example {
# Boolean -
isMale ? TRUE
# DataSet -
subBlock {
item - simple text.
}
# Date -
dateWriten @ 2013-02-27 12:00:25.3789Z
# Double -
weight $ 75.3
# Integer -
age % 42
# Long -
big = 123456
# String [without reserved characters] -
name - William Norman-Walker
# String [with reserved chracters] -
longText \
\# this will all be read as\
it \@ contains all the special chracters \\ escaped\
\{ so the parser can read them \}.\
all for \$15.00 \@ \-50\% discount
# An array can be a list of any supported objects
array [
- each element in an array
- will appear on a different line
! 123
- data types can be mixed
]
}
它有点像我所知道的其他几种格式;但这就是它的演变方式。对于简单的配置文件,类型字符可以省略,并被视为字符串。扩展格式实际上是多余的,只需在行终止符上使用反斜杠就足以干净地解析它了。
使用代码
那么我如何使用它呢?第一种方式是加载配置选项。我将应用程序的配置存储在数据文件中,然后在启动时将其加载。
/**
* Standard Entry point.
* @param args
*/
public static void main (String[] args) {
DataReader reader = null;
try {
reader = new DataReader(new File("demmo.config"));
ConfigData config = new ConfigData(reader.read());
App app = new Appendable(config);
config.close();
} catch (DataException ex) {
System.err.print(ex);
} catch (java.io.IOException ex) {
System.err.print(ex);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
System.err.print(ex);
}
}
}
}
我不太喜欢抛出异常,但在这里我认为这样做是有意义的。考虑一下,ConfigData
的构造函数接受一个文件并处理所有底层问题。
数组
很明显,我需要数组。作为使用 MongoDb 后端的一部分,我发现从 JSON 转换为更安全的 DataSet 存在很多问题,所以我决定迎难而上。
要将数组作为项目添加,首先需要将其放入 ValueArray 中,这样可以确保数据得到正确管理。
数组中的值必须符合普通的值类型,并且它们本身可以是数组,但目前不支持 NULL。这将来可能会改变。
要添加一个数组,代码简洁明了
// Add the array:
int[] arr = {1, 2, 3};
data.put("first", new ValueArray(arr));
ValueArray va = new ValueArray();
va.add("X");
va.add(42);
data.put("second", va);
// or add to an exisiting one
data.getArray("first").add("Something");
链式调用
作为添加数组支持的一部分,我引入了链式调用的概念。对结构进行任何添加都将导致返回添加的组件,这允许使用链式命令。链式命令意味着我们可以替换
DataSet data = new DataSet();
data.put("A", 1);
ValueArray vaB = new ValueArray();
vaB.add("One");
vaB.add(2);
vaB.add(9876543210L);
vaB.add(3.4);
vaB.add(false);
ValueArray innerArray = new ValueArray();
innerArray.add(1);
innerArray.add(2);
vaB.add(innerArray);
DataSet innerData = new DataSet();
innerData.put("key","value");
vaB.add(innerData);
vaB.add(new java.util.Date())
data.put("B", vaB);
DataSet subset = new DataSet();
subset.put("D", null)
subset.put("E", "1\\2#3-4\"5{6}7?8$9@a%b\nc")
subset.put("F", "So-long, farewell Adure!")
data.put("C", subset);
用这个
DataSet data = new DataSet()
.put("A", 1)
.put("B", new ValueArray()
.add("One")
.add(2)
.add(9876543210L)
.add(3.4)
.add(false)
.add(new ValueArray(1,2))
.add(new DataSet().put("key","value"))
.add(new java.util.Date())
)
.put("C", new DataSet()
.put("D", null)
.put("E", "1\\2#3-4\"5{6}7?8$9@a%b\nc")
.put("F", "So-long, farewell Adure!")
);
我知道我更喜欢哪种!
关注点
我在编写的东西中经常使用这些类。这篇文章的目的是将来不必再重新审视这一点。对于那些感兴趣的人,下一篇文章应该是一个使用 DataSet
查找变量名的表达式求值器。
历史
- 2013-03 - 提交到 CodeProject 的初始版本。
- 2013-03-13 - 更新代码,修复了
DataItem
的toString()
中的错误。 - 2013-07-11 - 更新代码,修复了
SealedDataItem
中的get(key)
中的错误。 - 2015-03-10 - 添加了对值数组的支持以及必要的重构。