IonWriter

时间:2020-01-09 10:35:45  来源:igfitidea点击:

IAP工具中的IonWriter类(com.nanosai.gridops.ion.write.IonWriter)可以将ION字段写入字节数组。使用" IonWriter",我们可以自由地编写ION字段,因此要确保我们遵循所编写的ION字段的规则。这也意味着我们可以在封闭环境中违反规则(例如,对于特定数据文件,或者在不向公众开放的两个服务之间交换的消息)。

" IonWriter"包含两组方法。第一组是其实例方法。要使用这些方法,我们必须创建IonWriter的实例。第二组是" IonWriter"的静态方法。这些可以直接调用而无需IonWriter实例。

" IonWriter"实例方法比静态方法更易于使用。当我们创建" IonWriter"实例时,我们将" byte"数组设置为所有写入的ION字段的目标。 " IonWriter"会为我们跟踪到目标" byte"数组中的索引。静态方法需要将所有相关信息作为参数传递,例如目标"字节"数组,此数组中的偏移量(用于将ION字段写入)以及要写入的值。

以下各节将说明" IonWriter"的实例方法和静态方法。

IonWriter实例方法

首先,我们将看看" IonWriter"的实例方法,因为它们是最易于使用的。

创建一个IonWriter

要使用" IonWriter"实例方法,我们必须首先创建" IonWriter"实例,并将其添加到目标"字节"数组(写入ION字段的数组)中。这是创建IonWriter实例的方法:

byte[]    dest   = new byte[10 * 1024];

IonWriter writer = new IonWriter(dest, 0);

本示例创建一个" IonWriter"实例,将目标" byte"数组和起始偏移量作为参数传递给构造函数。从指定的偏移量开始,所有ION字段都将写入到指定的" byte"数组中。

我们还可以使用setDestination()方法在" IonWriter"上设置目标"字节"数组,如下所示:

byte[]    dest   = new byte[10 * 1024];

IonWriter writer = new IonWriter();

writer.setDestination(dest, 0);

一旦在" IonWriter"上设置了目标,就可以开始将ION字段写入目标" byte"数组。

writeBytes()

writeBytes()方法将一系列原始字节作为ION Bytes字段写入目标字节数组。这是一个" IonWriter"writeBytes()示例:

byte[] value = new byte[]{ 1, 2, 3 };
    
writer.writeBytes(value);

writeBytes()方法还支持将null数组写出,在这种情况下,将写入长度为0的ION Bytes字段(代表一个null Bytes字段)。

writeBoolean()

writeBoolean()方法将Java布尔值作为ION Tiny字段写入目标字节数组。这是一个" IonWriter"的" writeBoolean()"示例:

boolean value = true;

writer.writeBoolean(value);

writeBooleanObj()

writeBooleanObj()方法将Java布尔对象作为ION Tiny字段写入目标字节数组。这是一个" IonWriter"的" writeBooleanObj()"示例:

Boolean value = new Boolean(true);

writer.writeBooleanObj(value);

writeBooleanObj()方法还支持将null写入,在这种情况下,将写入长度为0的ION Tiny字段(表示null null的Tiny字段)。

writeInt64()

writeInt()方法将Java long(或者其他整数)作为Int64-Positive或者Int64-Negative字段写入目标字节数组。这是一个" IonWriter"的writeInt64()示例:

long value = 123456;

writer.writeInt64(value);

writeInt64Obj()

writeInt64Obj()方法将Java Long对象作为Int64-Positive或者Int64-Negative字段写入目标字节数组。这是一个" IonWriter"的writeInt64()示例:

Long value = new Long(123456);

writer.writeInt64Obj(value);

writeInt64Obj()方法还支持将null写入,在这种情况下,将写入长度为0的ION Int64-Positive字段(表示一个null的Int64-Positive字段)。

writeFloat32()

" writeFloat32()"方法将Java" float"作为ION Float字段写入目标" byte"数组。这是一个" IonWriter"的writeFloat32()示例:

float value = 123.123f;

writer.writeFloat32(value);

writeFloat32Obj()

writeFloat32Obj()方法将Java Float对象作为ION Float字段写入目标字节数组。这是一个" IonWriter"writeFloat32Obj()示例:

Float value = new Float(123.123f);

writer.writeFloat32Obj(value);

writeFloat32Obj()方法还支持将null写入,在这种情况下,将写入长度为0的ION Float字段(表示一个null浮点字段)。

writeFloat64()

writeFloat64()方法将Java double作为ION Float字段写入目标字节数组。这是一个" IonWriter"的writeFloat64()示例:

double value = 123.123d;

writer.writeFloat64(value);

writeFloat64Obj()

writeFloat64Obj()方法将Java Double对象作为ION Float字段写入目标byte数组。这是一个" IonWriter"writeFloat64Obj()示例:

Double value = new Double(123.123d);

writer.writeFloat64Obj(value);

writeFloat64Obj()方法还支持将null写入,在这种情况下,将写入长度为0的ION Float字段(表示一个null浮点字段)。

writeUtf8()

writeUtf8()方法将Java字符串或者UTF-8编码字节的字节数组作为ION UTF-8或者UTF-8-Short字段写入目标字节数组。如果要写入的UTF-8字节中有15个或者更少的字节,则将写入UTF-8-Short字段。否则,将写入UTF-8字段。

这是两个IonWriter``writeUtf8()的例子:

String value = "Hello";

writer.writeUtf8(value);

byte[] valueBytes = "Hello".getBytes("UTF-8") ;

writer.writeUtf8(valueBytes);

这两个writeUtf8()方法也都支持将null写入,在这种情况下,将写入长度为0的ION UTF-8字段(代表一个null UTF-8字段)。

如果作为参数传递的"字符串"或者"字节"数组具有0个字符/元素,则将以一个长度为0的长度字节写入UTF-8字段(两个字节共1个前导字节+ 1个长度字节)。这就是将空字符串与null值区分开的方式。

writeUtc()

writeUtc()方法将Java"日历"作为ION UTC字段写入给定的" byte"数组。这是一个" IonWriter"的writeUtc()示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

Calendar value = new GregorianCalendar();
value.setTimeZone(TimeZone.getTimeZone("UTC"));
int dateTimeLength = 7;  //yyMdhms
writer.writeUtc(value, dateTimeLength);

ION UTC字段可以包含使用ION特定字节编码进行编码的日期和时间。编码的长度会有所不同,具体取决于我们要包含的信息量。如果我们看上面的示例,则writeUtc()方法的最后一个参数指定要使用的日期时间格式的"长度"。该示例使用7个字节的长度。在下表中,我们可以看到有效的长度以及它们导致的日期时间格式:

长度描述
2两个字节,表示介于0到65535之间的年份。没有更多信息。
3为长度2,后面添加一个字节,表示一年中的月份(从1到12)。
4为长度3,并添加了一个字节,表示一个月中的某天(从1到31)。
5在一天中的小时(从0到23)后面加上一个长度为4的字节。
6为长度5,并添加了一个代表分钟的字节(从0到59)。
7为长度6,并添加了代表秒的字节(引入leap秒时从0到59-60)。
9为长度7,并添加了两个字节,代表毫秒(0到999)。
10为长度7,并添加了三个字节,表示微秒(0到999,999)。
11为长度7,添加了四个字节字节,代表纳秒(0到999,999,999)。

关于毫秒,微秒和纳秒,日期时间格式只能包含以下信息之一。我们不能在ION UTC字段中同时发送毫秒,微秒和纳秒(或者其任何组合)。但是,我们可以从纳秒中获取微秒,并可以从微秒中获取毫秒。那应该足够了。

如我们所见,ION UTC字段编码类似于标准的ISO日期时间格式,除了ION UTC编码使用二进制数字(字节)而不是文本数字(字符)来表示日期中的数字。

另外,必须始终以UTC时间发送UTC日期。不允许使用本地日期/时间,因此也没有时区。在任一端都可以轻松地完成与本地日期和时间之间的转换。因此,传递给writeUtc()方法的Calendar必须在调用writeUtc()之前的UTC时间。

这种紧凑的编码使ION UTC字段占用了相应ISO日期时间文本格式使用的字节数的大约50%。少了一点,实际上是因为ISO日期时间格式在日期和时间之间具有T字符,并且使用Z或者5个字符对时区进行编码。

除了比ISO日期时间格式更紧凑之外,ION UTC字段还可以更快地进行编码/解码。二进制数字比文本数字的解码速度更快,尤其是当它们由较少的字节组成时。

writeObjectBegin()+ writeObjectEnd()

writeObjectBegin()和writeObjectEnd()方法用于写入ION Object字段的开头和结尾。实际上,ION对象字段没有"结束"标记。 writeObjectEnd()方法实际上跳到ION Object字段的开头并更新其长度字节(在前导字节之后)。

这是一个如何使用writeObjectBegin()和writeObjectEnd()的示例:

byte[] dest = new byte[10 * 1024];
IonWriter writer = new IonWriter(dest, 0);

int objectStartIndex = writer.destIndex;

int lengthLength = 2;
writer.writeObjectBegin(lengthLength);

writer.writeKey("field0");
writer.writeInt64(123);

writer.writeKey("field1");
writer.writeInt64(456);

int objectBodyLength = writer.destIndex - (objectStartIndex + 1 + lengthLength);

writer.writeObjectEnd(objectStartIndex, lengthLength, objectBodyLength);

请注意,我们必须如何将lengthLength参数传递给writeObjectBegin()。此参数表明要保留多少字节来写入对象的长度。在以后的对writeObjectEnd()的调用中,将对象的长度填充到这些保留的长度字节中。

该示例保留2个字节来表示长度,因为我们知道Object字段主体的长度将永远不会超过2 ^ 16 1(65,535)个字节。实际上,在这种情况下,保留单个长度的字节就足够了,因为"对象"字段的主体长度将不超过255个字节。

还要注意writeKey()调用。将ION Key字段写入ION Object表示密钥名称或者属性名称。键字段后的ION字段是该键或者属性的值。

writeTableBegin()+ writeTableEnd()

writeTableBegin()和writeTableEnd()方法的工作方式与writeObjectBegin()和writeObjectEnd()方法的工作相似。 writeTableBegin()方法写入ION Table字段的开头,而writeTableEnd()方法跳转并更新Table字段的保留长度字节。

这是一个如何使用writeTableBegin()和writeTableEnd()方法的示例:

byte[] dest = new byte[10 * 1024];
IonWriter writer = new IonWriter(dest, 0);

int tableStartIndex = writer.destIndex;
int lengthLength = 2;
writer.writeTableBegin(lengthLength);

writer.writeKey("field0");
writer.writeKey("field1");

writer.writeInt64(123);
writer.writeInt64(456);

writer.writeInt64(111);
writer.writeInt64(222);

int tableBodyLength = writer.destIndex - (tableStartIndex + 1 + lengthLength);

writer.writeTableEnd(tableStartIndex, lengthLength, tableBodyLength);

注意,我们必须如何将一个lengthLength参数传递给writeTableBegin()。此参数表明要保留多少字节来写入Table字段的长度。在随后的对writeTableEnd()的调用中,表的长度被填充为这些保留的长度字节。

该示例保留2个字节来表示长度,因为我们知道Table字段主体的长度将永远不会超过2 ^ 16 1(65,535)个字节。实际上,在这种情况下,保留单个长度的字节就足够了,因为Table字段主体的长度将不超过255个字节。

还要注意与编写ION Object字段的一个重要区别。 ION Table字段仅包含一次列的键。就像CSV文件的标题行一样。然后,该示例总共写入了4个值字段(2个Int64和2个Float)。这些值字段根据均被写入的顺序与标题相关联。第一个值字段与第一键字段相关,第二个值字段与第二键等

当我们获得的值字段序列号大于"键"字段的数量时,我们将从第一个"键"字段重新开始重新匹配(键索引返回0),然后匹配从该位置继续继续。因此,第三值字段与第一键字段匹配,并且第四值字段与第二键字段匹配。

我们应该确保始终有足够的值字段与键字段的整行匹配。如果表有4个键,则应该有4个值字段的多个(0、4、8、12等)。

注意:我们仍在考虑ION Table字段是否应该包含Table中的行数作为Table中的第一元素,就像Arrays一样。最终可能会是这种情况。如果我们稍作更改,那么上面的代码示例将有所更改。通过查看ION数组的编写方式,我们可以了解有关方法。

writeArrayBegin()+ writeArrayEnd()

静态的writeArrayBegin()和writeArrayEnd()写入ION Array字段的开头和结尾。实际上,ION Array没有"结束"标记。取而代之的是,writeArrayEnd()跳转到数组的开头,并将数组主体的长度(以字节为单位)写入保留的长度字节。

这是有关如何编写ION Array字段的示例:

byte[] dest = new byte[10 * 1024];
IonWriter writer = new IonWriter(dest, 0);

int arrayStartIndex = writer.destIndex;
int lengthLength = 2;
writer.writeArrayBegin(lengthLength);
writer.writeInt64(3); //element count

writer.writeInt64(456);
writer.writeInt64(111);
writer.writeInt64(222);

int arrayBodyLength = writer.destIndex - (arrayStartIndex + 1 + lengthLength);

writer.writeArrayEnd(arrayStartIndex, lengthLength, arrayBodyLength);

请注意,"数组"字段没有键。 ION阵列旨在用于相同类型的ION字段的"匿名"序列。是的,我们实际上可以混合写入ION Array的字段类型,但这不是打算使用ION Array字段的方式。如果我们要使用一系列混合字段类型,请使用ION对象中不包含Key字段(允许)。

还要注意,写入数组的第一个ION字段是如何包含数组中元素数量的Int64-Positive的。对于小于16个元素的数组,还允许使用Tiny字段而不是Int64-Positive字段。

如果我们不知道提前多少个数组中的元素,请保留一定数量的字节以供以后保存数组长度(1个前导字节+ N个字节来保存元素计数)。这类似于稍后将数组的字节长度填充到保留的长度字节中的方式。我们可以始终保留与保留数组的字节长度相同的字节数,因为数组中的元素永远不能超过其内部的字节数(最小的ION字段为1字节长)。

IonWriter静态方法

" IonWriter"的静态方法反映了" IonWriter"的实例方法。以下各节介绍了静态方法。

writeBytes()

静态的writeBytes()方法将原始字节序列(数组)作为Bytes ION字段写入给定的字节数组。这是一个" IonWriter"writeBytes()示例:

byte[] src  = new byte[128];

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

int bytesWritten = IonWriter.writeBytes(dest, destOffset, src);

writeBytes()方法返回写入byte数组的字节总数,包括Bytes ION字段的前导字节,长度字节和源数组的原始字节。

writeBoolean()

静态的writeBoolean()方法以给定的偏移量将boolean ION字段(编码为Tiny)写入给定的byte数组。这是一个" IonWriter"的" writeBoolean()"示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

int bytesWritten = IonWriter.writeBoolean(dest, destOffset, true);

"布尔"被编码为"微小"离子场。 Tiny字段始终总共占用1个字节。

writeInt64()

静态方法writeInt64()将最长64位的整数写入给定的byte数组。根据给定值是正值还是负值,使用的ION字段将为Int64-Positive或者Int64-Negative。这是一个" IonWriter"writeInt64()示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

int value = 123456;
int bytesWritten = IonWriter.writeInt64(dest, destOffset, value);

writeInt64()方法返回写入字节数组的字节总数。 Int64阳性/ Int64阴性ION字段的长度最多为64位(8字节),外加Lead字段的一个额外字节。因此,总共2到9个字节。

writeFloat32()

静态的writeFloat32()方法将32位浮点(Java float)作为ION Float字段写入给定的字节数组。这是一个" IonWriter"的writeFloat32()示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

float value = 12.34f;
int bytesWritten = IonWriter.writeFloat32(dest, destOffset, value);

Java" float"总是写为ION Float字段,该字段为4字节(32位)加上1个前导字节,表示总共5个字节。

writeFloat64()

静态的writeFloat64()方法将64位浮点(Java double)写入给定的byte数组。这是一个" IonWriter"的writeFloat64()示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

double value = 1234.5678d;
int bytesWritten = IonWriter.writeFloat64(dest, destOffset, value);

Java" double"总是写为8个字节(64位)加上1个前导字节的ION Float字段,表示总共9个字节。

writeUtf8()

静态的writeUtf8()方法将Java String写入ION UTF-8字段。这是一个" IonWriter"的writeUtf8()示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

String value = "A String";
int bytesWritten = IonWriter.writeUtf8(dest, destOffset, value);

" IonWriter"还包含另一个版本的" writeUtf8()",该版本采用"字节"数组作为参数,而不是"字符串"。 " byte"数组中的字节必须已经被编码为UTF-8. writeUtf8()方法不会触及这些字节。它们仅被复制到目标字节数组。以下是使用该writeUtf8()方法的示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

byte[] value = "A String".getBytes("UTF-8");
int bytesWritten = IonWriter.writeUtf8(dest, destOffset, value);

在将字符串写入目标byte数组之前,先将其转换为UTF-8编码的字节。如果UTF-8编码的字节总数不超过15,则将它们写为ION UTF-8-Short字段。如果UTF-8编码的字节总数为16个或者更多,则将它们写为ION UTF-8字段。

writeUtc()

静态的writeUtc()方法将Java日历作为ION UTC字段写入给定的byte数组。这是一个" IonWriter"的writeUtc()示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

Calendar value = new GregorianCalendar();
value.setTimeZone(TimeZone.getTimeZone("UTC"));
int dateTimeLength = 7;
int bytesWritten = IonWriter.writeUtc(dest, destOffset, value, dateTimeLength);

ION UTC字段可以包含使用ION特定字节编码进行编码的日期和时间。编码的长度会有所不同,具体取决于我们要包含的信息量。如果查看上面的示例,则writeUtc()方法的最后一个参数指定要使用的日期时间格式的"长度"。该示例使用7个字节的长度。在下表中,我们可以看到有效的长度以及它们导致的日期时间格式:

长度描述
2两个字节,表示介于0到65535之间的年份。没有更多信息。
3为长度2,后面添加一个字节,表示一年中的月份(从1到12)。
4为长度3,并添加了一个字节,表示一个月中的某天(从1到31)。
5在一天中的小时(从0到23)后面加上一个长度为4的字节。
6为长度5,并添加了一个代表分钟的字节(从0到59)。
7为长度6,并添加了代表秒的字节(引入leap秒时从0到59-60)。
9为长度7,并添加了两个字节,代表毫秒(0到999)。
10为长度7,并添加了三个字节,表示微秒(0到999,999)。
11为长度7,添加了四个字节字节,代表纳秒(0到999,999,999)。

关于毫秒,微秒和纳秒,日期时间格式只能包含以下信息之一。我们不能在ION UTC字段中同时发送毫秒,微秒和纳秒(或者其任何组合)。但是,我们可以从纳秒中得出微秒,而可以从微秒中得出毫秒。那应该足够了。

如我们所见,ION UTC字段编码类似于标准的ISO日期时间格式,除了ION UTC编码使用二进制数字(字节)代替文本数字(字符)来表示日期中的数字。

另外,必须始终以UTC时间发送UTC日期。不允许使用本地日期/时间,因此也没有时区。在任一端都可以轻松地完成与本地日期和时间之间的转换。因此,传递给writeUtc()方法的Calendar必须在UTC时间之前调用writeUtc()

这种紧凑的编码使ION UTC字段占用了相应ISO日期时间文本格式使用的字节数的大约50%。少了一点,实际上是因为ISO日期时间格式在日期和时间之间具有T字符,并且使用Z或者5个字符对时区进行编码。

除了比ISO日期时间格式更紧凑之外,ION UTC字段还可以更快地进行编码/解码。二进制数字比文本数字的解码速度更快,尤其是当它们由较少的字节组成时。

编写多个字段

当将多个ION字段依次写入同一字节数组时,我们需要使用最后写入的ION字段之后的第一个字节的索引作为下一个ION字段的起始索引。这是一个Java示例,向我们展示如何执行此操作:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

int index = 0;
index += IonWriter.writeInt64  (dest, destOffset, 123456);
index += IonWriter.writeFloat32(dest, destOffset, 123.456f);
index += IonWriter.writeFloat64(dest, destOffset, 987.654d);

写作复杂领域

复杂ION字段是可以其中包含其他字段的字段。三种最常用的复杂ION字段是"对象","表"和"数组"。实际上,我们也可以将ION字段嵌套在Bytes字段中,因为Bytes字段可以容纳包括ION字段在内的所有原始字节。

所有ION字段在其开头都包含其字节长度。复杂字段的挑战是,我们通常不提前知道嵌套在复杂字段中的字段将占用多少字节。在这种情况下,我们不能在复数字段的开头在其主体中写入字节数。

为了解决这个问题,我们有一个小技巧。我们无需保留其主体占用复数字段开头的字节数,而只是保留足以容纳该长度的字节数。完成将字段写入复杂字段的主体后,我们知道这些字段总计多少字节。然后,我们可以跳回去并将长度写到保留的长度字节中。

我们应该保留多少个长度字节?如果我们知道复杂字段的主体永远不会超过255个字节,请保留1个长度字节。如果正文的长度永远不会超过65,535个字节,则保留2个字节。如果正文的长度永远不会超过16,777,216字节,请保留3个字节,等等。ION字段最多可以包含15个长度的字节,但是我们通常永远不需要超过4个字节。

用于表示ION字段的长度(以字节为单位)的字节数通常称为" lengthLength"。这意味着表示ION字段主体的"长度"(以字节为单位)所需的"长度"(以字节为单位)。如果ION字段的值(正文)长度小于256个字节,则lengthLength为1(字节)就足够了。如果ION字段的值长度小于65,536字节,则lengthLength为2(字节)就足够了,依此类推。

复杂字段的写方法将保留和稍后填充lengthLength字节。

writeObjectBegin()+ writeObjectEnd()

静态的writeObjectBegin()和writeObjectEnd()方法用于写入ION Object字段的开头和结尾。实际上,ION对象字段没有"结束"标记。 writeObjectEnd()方法实际上跳到ION Object字段的开头并更新其长度字节(在前导字节之后)。

这是一个如何使用writeObjectBegin()和writeObjectEnd()的示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

int index = 0;

int objectStartIndex = index;
int lengthLength = 2;

index += IonWriter.writeObjectBegin(dest, index, lengthLength);

index += IonWriter.writeKey(dest, index, "field0");
index += IonWriter.writeInt64  (dest, destOffset, 123456);

index += IonWriter.writeKey(dest, index, "field1");
index += IonWriter.writeFloat32(dest, destOffset, 123.456f);

// index - objectStartIndex = full byte length of object

int objectFullByteLength = index - objectStartIndex;

// Length of the value (body) of an object should not include
// the ION field lead byte (1 byte) or the length bytes (lengthLength number of bytes)

int objectBodyByteLength = objectFullByteLength - 1 - lengthLength;

IonWriter.writeObjectEnd(dest, objectStartIndex, lengthLength, objectBodyByteLength);

请注意,我们必须如何将lengthLength参数传递给writeObjectBegin()。此参数表明要保留多少字节来写入对象的长度。在以后的对writeObjectEnd()的调用中,将对象的长度填充到这些保留的长度字节中。

该示例保留2个字节来表示长度,因为我们知道Object字段主体的长度将永远不会超过2 ^ 16 1(65,535)个字节。实际上,在这种情况下,保留单个长度的字节就足够了,因为"对象"字段的主体长度将不超过255个字节。

还要注意writeKey()的调用。将ION Key字段写入ION Object表示密钥名称或者属性名称。键字段后的ION字段是该键或者属性的值。

writeTableBegin()+ writeTableEnd()

静态的writeTableBegin()和writeTableEnd()方法的工作方式与writeObjectBegin()和writeObjectEnd()方法的工作原理类似。 writeTableBegin()方法写入ION Table字段的开头,而writeTableEnd()方法跳转并更新Table字段的保留长度字节。

这是一个如何使用writeTableBegin()和writeTableEnd()方法的示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

int index = 0;

int tableStartIndex = index;
int lengthLength = 2;

index += IonWriter.writeTableBegin(dest, index, lengthLength);

index += IonWriter.writeKey(dest, index, "field0");
index += IonWriter.writeKey(dest, index, "field1");

index += IonWriter.writeInt64  (dest, destOffset, 123456);
index += IonWriter.writeFloat32(dest, destOffset, 123.456f);

index += IonWriter.writeInt64  (dest, destOffset, 123456);
index += IonWriter.writeFloat32(dest, destOffset, 123.456f);

// index - tableStartIndex = full byte length of Table field

int tableFullByteLength = index - tableStartIndex;

// Length of the value (body) of an Table should not include
// the ION field lead byte (1 byte) or the length bytes (lengthLength number of bytes)

int tableBodyByteLength = tableFullByteLength - 1 - lengthLength;

IonWriter.writeTableEnd(dest, tableStartIndex, lengthLength, tableBodyByteLength);

注意,我们必须如何将一个lengthLength参数传递给writeTableBegin()。此参数表明要保留多少字节来写入Table字段的长度。在随后的对writeTableEnd()的调用中,表的长度被填充为这些保留的长度字节。

该示例保留2个字节来表示长度,因为我们知道Table字段主体的长度将永远不会超过2 ^ 16 1(65,535)个字节。实际上,在这种情况下,保留单个长度的字节就足够了,因为Table字段主体的长度将不超过255个字节。

还要注意与编写ION Object字段的一个重要区别。 ION Table字段仅包含一次列的键。就像CSV文件的标题行一样。然后,该示例总共写入了4个值字段(2个Int64和2个Float)。这些值字段根据都被写入的顺序与标题相关联。第一值字段与第一键字段相关,第二值字段与第二键等

当我们获得的值字段序列号大于"键"字段的数量时,我们将从第一个"键"字段重新开始重新匹配(键索引返回0),然后匹配从该位置继续继续。因此,第三值字段与第一键字段匹配,并且第四值字段与第二键字段匹配。

我们应该确保始终有足够的值字段与键字段的整行匹配。如果表有4个键,则应该有4个值字段(0、4、8、12等)的倍数。

注意:我们仍在考虑ION Table字段是否应该包含Table中的行数作为Table中的第一元素,就像Arrays一样。最终可能会是这种情况。如果我们稍作更改,那么上面的代码示例将有所更改。通过查看ION数组的编写方式,我们可以了解有关方法。

writeArrayBegin()+ writeArrayEnd()

静态的writeArrayBegin()和writeArrayEnd()写入ION Array字段的开头和结尾。实际上,ION Array没有"结束"标记。取而代之的是,writeArrayEnd()跳转到数组的开头,并将数组主体的长度(以字节为单位)写入保留的长度字节。

这是有关如何编写ION Array字段的示例:

byte[] dest = new byte[10 * 1024];
int destOffset = 0;

int index = 0;

int arrayStartIndex = index;
int lengthLength = 2;

index += IonWriter.writeArrayBegin(dest, index, lengthLength);

// element count obligatory, and must come before element fields.
int arrayElementCount = 3;
index += IonWriter.writeInt64  (dest, destOffset, arrayElementCount);

// array elements.
index += IonWriter.writeInt64  (dest, destOffset, 123);
index += IonWriter.writeInt64  (dest, destOffset, 456);
index += IonWriter.writeInt64  (dest, destOffset, 789);

// index - arrayStartIndex = full byte length of Table field,
int arrayFullByteLength = index - arrayStartIndex;

// Length of the value (body) of an Array should not include
// the ION field lead byte (1 byte) or the length bytes (lengthLength number of bytes)
int arrayBodyByteLength = arrayFullByteLength - 1 - lengthLength;

IonWriter.writeArrayEnd(dest, arrayStartIndex, lengthLength, arrayBodyByteLength);

请注意,"数组"字段没有键。 ION阵列旨在用于相同类型的ION字段的"匿名"序列。是的,我们实际上可以混合写入ION Array的字段类型,但这不是打算使用ION Array字段的方式。如果我们要使用一系列混合字段类型,请使用ION对象中不包含Key字段(允许)。

还要注意,写入数组的第一个ION字段是如何包含数组中元素数量的Int64-Positive的。对于小于16个元素的数组,还允许使用Tiny字段而不是Int64-Positive字段。

如果我们不知道提前多少个数组中的元素,请保留一定数量的字节以供以后保存数组长度(1个前导字节+ N个字节来保存元素计数)。这类似于稍后将数组的字节长度填充到保留的长度字节中的方式。我们可以始终保留与保留数组的字节长度相同的字节数,因为数组中的元素永远不能超过其内部的字节数(最小的ION字段为1字节长)。