clemensv / cloudeventsformatdemo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CloudEvents Extensibility Serialization

The code in this repo exists to illustrate how various different serialization models can leverage their respective strengths and deal with their respective constraints while the base specification retains a "flat" data model without a specific extensibility bucket.

I have not added cases where the JSON and XML objects are read dynamically since those ought to be obvious. The strength of the flat model is that dynamic code which simply reads a JSON object into a dictionary doesn't break with a flat model and does break when a formar extension is promoted to a top-level construct when using a model with a bag. If anyone needs to have that clearly illustrated, I can add that.

The code is written in .NET Core C# and uses four different .NET serialization libraries. For comparability, all libraries use the attribute-declarative model to drive the serializer.

The application defines a base interface for CloudEvents 1.0 and a derived [base interface](base interface) for a fictious version 1.1. The latter extends the well-known set of fields by two fields, extraThing1 and extraThing2.

JSON

Main file is Program.cs.

The initial object is created as a 1.0 object, with five extensions added into a bag in the in-memory serializer object for JSON. The object is then serialized to JSON, where the extensions show up as flat object members of the envelope.

When the object is deserialized into 1.0, the elements go back into the bag. That's simply a function of the chosen serializer, JSON.NET.

When the object is deserialized into 1.1, the extraThing1 and extraThing2 properties and populated from the content, and the remaining extensions go into the bag. Again, that's a function of the serializer.

Because the wire repersentation doesn't make a difference between the extensions in 1.0 and 1.1, you'll see that serializing the resulting 1.1 in-memory object and reading it back as 1.0 or 1.1 yields the exact same expected results.

XML

The XML CloudEvents 1.0 in-memory object is created as a deep copy from the JSON 1.1 object, showing that there's nothing precluding extensions to be copied across different objects tailored to different serializers.

The rest of the XML flow is exactly equivalent to JSON. The data on the wire shows up flat and we're using the serializer's facility to deal with unknown objects to handle extensions.

Thrift and Protobuf

The Thrift and Protobuf handling is practically identical. The Thrift and Protobuf serializers are using an attribute driven model and not the respective IDL compiler, but the result is wire compatible.

The strategy here is to make an extension bag and for the in memory respresentation and to also use that bag on the wire, because both require schema.

Specifically noteworthy is the case where a Thrift/Protobuf CloudEvents 1.0 object is being deserialized into a 1.1 object. That case has a simple post-serialization step (declarative in the Protobuf case), that generically maps content from the extension bag to the fields added in 1.1, resulting in a clean upgrade path.

Neither of the two schema-bound models roundtrips such that you can losslessly deserialize a 1.1 object into a 1.0 object with extra fields being demoted back into extensions as both XML and JSON support. That's plainly a weakness of the external metadata model.

To deal with that, I suggest that we amend the Content-Type in the transport bindings (not the one for the data attribute) with a cloudEventsVersion parameters (e.g. application/cloudevents+protobuf;cloudEventsVersion=1.1) which allows a schema bound serializer to pick the right base schema.

Output, so that you don't have to run it yourself:


--- JSON 1.0
{
  "cloudEventsVersion": "1.0",
  "eventTypeVersion": "1.0",
  "eventType": "test.type",
  "source": "urn:test",
  "eventID": "12345678",
  "eventTime": "2018-08-14T17:28:11.0470159Z",
  "schemaURL": null,
  "contentType": null,
  "data": null,
  "myextension": "abcdef",
  "yourextension": "ghijkl",
  "theirextension": "mnopqr",
  "extraThing1": "stuvwx",
  "extraThing2": "yzabcd"
}
JSON: ----- Deserialize as 1.0 > 1.0
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
1.0 extension extraThing1: stuvwx
1.0 extension extraThing2: yzabcd
JSON: ----- Deserialize 1.0 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.1 extension myextension: abcdef
1.1 extension yourextension: ghijkl
1.1 extension theirextension: mnopqr
--- JSON 1.1
{
  "extraThing1": "stuvwx",
  "extraThing2": "yzabcd",
  "cloudEventsVersion": "1.0",
  "eventTypeVersion": "1.0",
  "eventType": "test.type",
  "source": "urn:test",
  "eventID": "12345678",
  "eventTime": "2018-08-14T17:28:11.0470159Z",
  "schemaURL": null,
  "contentType": null,
  "data": null,
  "myextension": "abcdef",
  "yourextension": "ghijkl",
  "theirextension": "mnopqr"
}
JSON: ----- Deserialize as 1.1 > 1.0
1.0 extension extraThing1: stuvwx
1.0 extension extraThing2: yzabcd
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
JSON: ----- Deserialize 1.1 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.1 extension myextension: abcdef
1.1 extension yourextension: ghijkl
1.1 extension theirextension: mnopqr
--- XML 1.0
<?xml version="1.0" encoding="utf-8"?>
<CloudEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <cloudEventsVersion>1.0</cloudEventsVersion>
  <eventTypeVersion>1.0</eventTypeVersion>
  <eventType>test.type</eventType>
  <source>urn:test</source>
  <eventID>12345678</eventID>
  <eventTime>2018-08-14T17:28:11.0470159Z</eventTime>
  <extraThing1>stuvwx</extraThing1>
  <extraThing2>yzabcd</extraThing2>
  <myextension>abcdef</myextension>
  <yourextension>ghijkl</yourextension>
  <theirextension>mnopqr</theirextension>
</CloudEvent>
XML: ----- Deserialize 1.0 > 1.0
1.0 extension extraThing1: stuvwx
1.0 extension extraThing2: yzabcd
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
XML: ----- Deserialize 1.0 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.1 extension myextension: abcdef
1.1 extension yourextension: ghijkl
1.1 extension theirextension: mnopqr
--- XML 1.1
<?xml version="1.0" encoding="utf-8"?>
<CloudEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <cloudEventsVersion>1.0</cloudEventsVersion>
  <eventTypeVersion>1.0</eventTypeVersion>
  <eventType>test.type</eventType>
  <source>urn:test</source>
  <eventID>12345678</eventID>
  <eventTime>2018-08-14T17:28:11.0470159Z</eventTime>
  <myextension>abcdef</myextension>
  <yourextension>ghijkl</yourextension>
  <theirextension>mnopqr</theirextension>
  <extraThing1>stuvwx</extraThing1>
  <extraThing2>yzabcd</extraThing2>
</CloudEvent>
XML: ----- Deserialize 1.1 > 1.0
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
1.0 extension extraThing1: stuvwx
1.0 extension extraThing2: yzabcd
XML: ----- Deserialize 1.1 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.1 extension myextension: abcdef
1.1 extension yourextension: ghijkl
1.1 extension theirextension: mnopqr
--- Thrift 1.0
0B 00 01 00 00 00 03 31 2E 30 0B 00 02 00 00 00  .......1.0......
03 31 2E 30 0B 00 03 00 00 00 09 74 65 73 74 2E  .1.0.......test.
74 79 70 65 0B 00 04 00 00 00 08 75 72 6E 3A 74  type.......urn:t
65 73 74 0B 00 05 00 00 00 08 31 32 33 34 35 36  est.......123456
37 38 0B 00 07 00 00 00 00 0B 00 08 00 00 00 00  78..............
0B 00 06 00 00 00 13 31 34 2E 30 38 2E 32 30 31  .......14.08.201
38 20 31 37 3A 32 38 3A 31 31 0D 00 0A 0B 0B 00  8 17:28:11......
00 00 05 00 00 00 0B 65 78 74 72 61 54 68 69 6E  .......extraThin
67 31 00 00 00 06 73 74 75 76 77 78 00 00 00 0B  g1....stuvwx....
65 78 74 72 61 54 68 69 6E 67 32 00 00 00 06 79  extraThing2....y
7A 61 62 63 64 00 00 00 0B 6D 79 65 78 74 65 6E  zabcd....myexten
73 69 6F 6E 00 00 00 06 61 62 63 64 65 66 00 00  sion....abcdef..
00 0D 79 6F 75 72 65 78 74 65 6E 73 69 6F 6E 00  ..yourextension.
00 00 06 67 68 69 6A 6B 6C 00 00 00 0E 74 68 65  ...ghijkl....the
69 72 65 78 74 65 6E 73 69 6F 6E 00 00 00 06 6D  irextension....m
6E 6F 70 71 72 00 00 00 00 00 00 00 00 00 00 00  nopqr...........

THRIFT: ----- Deserialize 1.0 > 1.0
1.0 extension extraThing1: stuvwx
1.0 extension extraThing2: yzabcd
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
THRIFT: ----- Deserialize 1.0 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
--- Thrift 1.1
0B 00 01 00 00 00 03 31 2E 30 0B 00 02 00 00 00  .......1.0......
03 31 2E 30 0B 00 03 00 00 00 09 74 65 73 74 2E  .1.0.......test.
74 79 70 65 0B 00 04 00 00 00 08 75 72 6E 3A 74  type.......urn:t
65 73 74 0B 00 05 00 00 00 08 31 32 33 34 35 36  est.......123456
37 38 0B 00 06 00 00 00 13 31 34 2E 30 38 2E 32  78.......14.08.2
30 31 38 20 31 37 3A 32 38 3A 31 31 0B 00 07 00  018 17:28:11....
00 00 00 0B 00 08 00 00 00 00 0D 00 0A 0B 0B 00  ................
00 00 03 00 00 00 0B 6D 79 65 78 74 65 6E 73 69  .......myextensi
6F 6E 00 00 00 06 61 62 63 64 65 66 00 00 00 0D  on....abcdef....
79 6F 75 72 65 78 74 65 6E 73 69 6F 6E 00 00 00  yourextension...
06 67 68 69 6A 6B 6C 00 00 00 0E 74 68 65 69 72  .ghijkl....their
65 78 74 65 6E 73 69 6F 6E 00 00 00 06 6D 6E 6F  extension....mno
70 71 72 0B 00 0B 00 00 00 06 73 74 75 76 77 78  pqr.......stuvwx
0B 00 0C 00 00 00 06 79 7A 61 62 63 64 00 00 00  .......yzabcd...
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

THRIFT: ----- Deserialize 1.1 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.1 extension myextension: abcdef
1.1 extension yourextension: ghijkl
1.1 extension theirextension: mnopqr
--- Protobuf 1.0
0A 03 31 2E 30 12 03 31 2E 30 1A 09 74 65 73 74  ..1.0..1.0..test
2E 74 79 70 65 22 08 75 72 6E 3A 74 65 73 74 2A  .type".urn:test*
08 31 32 33 34 35 36 37 38 32 0B 08 9E D0 B7 F2  .123456782...зò
AA 85 C1 36 10 05 3A 00 42 00 52 15 0A 0B 65 78  ª.Á6..:.B.R...ex
74 72 61 54 68 69 6E 67 31 12 06 73 74 75 76 77  traThing1..stuvw
78 52 15 0A 0B 65 78 74 72 61 54 68 69 6E 67 32  xR...extraThing2
12 06 79 7A 61 62 63 64 52 15 0A 0B 6D 79 65 78  ..yzabcdR...myex
74 65 6E 73 69 6F 6E 12 06 61 62 63 64 65 66 52  tension..abcdefR
17 0A 0D 79 6F 75 72 65 78 74 65 6E 73 69 6F 6E  ...yourextension
12 06 67 68 69 6A 6B 6C 52 18 0A 0E 74 68 65 69  ..ghijklR...thei
72 65 78 74 65 6E 73 69 6F 6E 12 06 6D 6E 6F 70  rextension..mnop
71 72  qr
PROTOBUF: ----- Deserialize 1.0 > 1.0
1.0 extension extraThing1: stuvwx
1.0 extension extraThing2: yzabcd
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
PROTOBUF: ----- Deserialize 1.0 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.0 extension myextension: abcdef
1.0 extension yourextension: ghijkl
1.0 extension theirextension: mnopqr
--- Protobuf 1.1
0A 03 31 2E 30 12 03 31 2E 30 1A 09 74 65 73 74  ..1.0..1.0..test
2E 74 79 70 65 22 08 75 72 6E 3A 74 65 73 74 2A  .type".urn:test*
08 31 32 33 34 35 36 37 38 32 0B 08 9E D0 B7 F2  .123456782...зò
AA 85 C1 36 10 05 3A 00 42 00 52 15 0A 0B 6D 79  ª.Á6..:.B.R...my
65 78 74 65 6E 73 69 6F 6E 12 06 61 62 63 64 65  extension..abcde
66 52 17 0A 0D 79 6F 75 72 65 78 74 65 6E 73 69  fR...yourextensi
6F 6E 12 06 67 68 69 6A 6B 6C 52 18 0A 0E 74 68  on..ghijklR...th
65 69 72 65 78 74 65 6E 73 69 6F 6E 12 06 6D 6E  eirextension..mn
6F 70 71 72 5A 06 73 74 75 76 77 78 62 06 79 7A  opqrZ.stuvwxb.yz
61 62 63 64  abcd
PROTOBUF: ----- Deserialize 1.1 > 1.1
1.1 builtin extraThing1 stuvwx
1.1 builtin extraThing2 yzabcd
1.1 extension myextension: abcdef
1.1 extension yourextension: ghijkl
1.1 extension theirextension: mnopqr

About


Languages

Language:C# 100.0%