DCodec Sample Code

  1. Get your schema. Generate a schema key (DKey).
    Don't have a schema yet? No problem, use no schema, or use JSON to create one at JSON2ASN.
    Schema must comply with the Dynamic Profile (check it at Analyzer).
  2. Get the DCodec. Create an instance of Oss.DCodec JSON or DER. Load the schema (optionally).
  3. Start encoding/decoding.
DCodec (Dynamic Codec) is capable of doing three things dynamically:
  1. Dynamic loading of a schema at runtime.
  2. Dynamic encoding and decoding of data that partially match the schema and/or the bindings.
  3. Dynamic bindings, i.e. the data objects that are created at runtime and support C# dynamic type.
DCodec deals with three kids of input/output objects:
BINDINGS (C#) SCHEMA (ASN.1) MESSAGE (JSON or DER)
dynamic, DValue, POCO (predefined) schema-less, schema-based streams, strings, byte arrays

// Hello World shows how to encode/decode/validate (serialize/deserialize as JSON or DER). 
// The data in this example is a "Hello World!" string, and the schema constrains 
// the string length to the range of 5..12 characters:
// 
// Schema DEFINITIONS AUTOMATIC TAGS::= BEGIN  
//   Type ::= SEQUENCE                            
//   {                                              
//      hello UTF8String (SIZE (5..12))  -- length in UTF8 chars
//   }                                              
// END  
        // load the schema (use https://asn1.io/dynamic/DKey.aspx to get a DKey for your schema)
        var schema = new Oss.DCodec.DSchema("Zb5qQllrUFRhNcBlGA22SiGfaZEk+xA0Jw2w+UPWz11J0VGfMQd6qYG3/xAAEA55VqDUvwpnQSCcnQpy2OntMZZS5rdQLsGLN04yyQ5Sbj88DzWwzWCXkGo2F9P5LnMh4NryPkl3BLpXdbaR8mrm0ux28HLgSuRB/B5ZiOjKxyOmA9uLmGpWPwqIH98C718Y1RxnIXurnLE5H0u01qwDpelujU+5YKG5CTHu3RvqVat5j2LHWWAKRiFxFJSMX2SngF27FjdnZzceDh19xndBJfBROnzOmcHRZ/KdiWP80SJKsRDpgRIUjzAy9yXXdSsZ9jzTpz00lEaXMfsbFXs8GJ2pxw/LDGkJfpcxFwg6OcovUwM4CvkvaLygjwPlGIndtYdcr5ezbibPN1i4sPRwNWNTOgf+6rnV8UHLi3N9FEfW5xO0BJRZlliv/7f+gDLmAPEqg7d3ZJn9FEpqqjrJpXL0PVCmFR1RuaT0Sw6YqgRtQCK/k473ClXaXg4OGh8btOmGOZsaS5HHPyBV5pyIpnJIo+gymDnYIOjB9KBhdaDk6xtEw4DZoiKR2Mc7MGzdpziU0coFbUaunWN5TTxlC/Ww81kpOvHkVAguqBdI7RnKiqszuziR42W/4TXeumLd0tEYRFxrUA==");
        var type = schema.FindType("Schema.Type");

        // create and set the data object (dynamically) 
        var dataOut = new Oss.DCodec.DValueObject() { {"hello", "Hello World"} }; // valid for the above schema, i.e. UTF8String (SIZE (5..12))

        // Create a codec (either JSON or DER)
        var codec = new Oss.DCodec.JsonCodec();  // or .DerCodec() 

        // NOTE: validation is possible in two ways: 1) during Encode()/Decode() or 2) by invoking Validate() 

        // Encode/Decode with the SchemaType argument will validate the data against the provided type
        var msg = codec.Encode(dataOut, type);

        // .... send/receive JSON or DER

        codec.DecoderOptions.ValidateConstraints = true; // Constraints validation during decoding is optional 
        dynamic dataIn = codec.Decode(msg, type);

        Console.WriteLine(dataIn.Hello.ToString());

        // Validating data directly
        string json = "{'hello':'Hello World!!!!!'}"; // too long
        var conflicts = codec.Validate(json, type); // validate a string or a data binding object
        foreach (var c in conflicts)
            Console.WriteLine("Invalid data: " + c);

        /* Output:
            *  Hello World
            *  Invalid data: ConstraintViolation: Schema.Type.hello value=Hello World!!!!! (constraint: Schema.Type(SIZE(5..12)); value size: 16)
        */
// This example shows how to decode/encode a JSON message without using a schema.
// Schema-less scenario is suitable for early stages of app/protocol development, 
// while data structures are still taking shape. 
// Once the data are more or less mature a schema can be defined for them 
// (e.g. by using this helper tool https://asn1.io/json2asn).
        var myCodec = new Oss.DCodec.JsonCodec();

        // Basic example: JSON message includes expected fields
        string json = "{ 'Name':'Falcon', 'Speed': { 'kmph':50000 }}";
        var rocket = myCodec.Decode(json);
        if (rocket["Speed"]["kmph"] > 28968 )
                Console.WriteLine(rocket["Name"] + " will leave the orbit..");
        /* output:
                Falcon will leave the orbit..
        */
        // More complex example: handling optional fields and special keys
        string jsonIn = "{ 'Name':'Falcon', 'Hi-Message':'Hi there', 'Speed': { 'mph':18000 }, 'Payload': [ 'car', 'GPS unit' ]} ";
            
		dynamic rocket2 = myCodec.Decode(jsonIn);
            
		// mandatory fields
        Console.WriteLine("Name: " + rocket2.Name); 
        Console.WriteLine("Message: " + rocket2["Hi-Message"]); // can't use dot notation (avoid special chars in JSON keys)
        Console.WriteLine("Speed: " + rocket2.Speed.mph.ToString()); 
            
		// optional fields
        if (rocket2.ContainsKey("Payload")) // checking the key existence for optional 
            foreach (var p in rocket2.Payload)
                Console.WriteLine("Payload: " + p);
        Console.WriteLine("Weight: " + (rocket2.Weight ?? "absent")); // Using operator ?? for optional
        Console.WriteLine("Launch: " + rocket2.Launch?.Location); // Using operator ?. for optional

        // modify and re-encode 
        rocket2["Hi-Message"] = "Hello";
        rocket2.Speed.mph = null; // use encoder option EncodeAbsentComponents to drop/include null fields
        rocket2.Speed.kmph = 0;   // this field is new/added.
            
		myCodec.EncoderOptions.EncodeAbsentComponents = false;
        string jsonOut = myCodec.Encode(rocket2);
            
		Console.WriteLine(jsonOut);
            
		/* output:
            Name: Falcon
            Message: Hi there
            Speed: 18000
            Payload: car
            Payload: GPS unit
            Weight: absent
            Launch:
            {"Name":"Falcon","Hi-Message":"Hello","Speed":{"kmph":0},"Payload":["car","GPS unit"]}
        */
        // Let's "explore" the data (#traverse, #iterate)
		string jsonMsg = "{ 'Name':'Falcon', 'Message':'Hi there', 'Speed': { 'mph':18000 }, 'Payload': [ 'car', 'GPS unit' ] }";
            
		var dv = myCodec.Decode(jsonMsg);
            
		foreach (var field in dv.Iterator.DescendantsAndSelf)
        {
            if (field.Value.DataType == Oss.DCodec.DDataType.Array)
                Console.WriteLine(field.Identifier + "(array):");
            else if (field.Value.DataType == Oss.DCodec.DDataType.Object)
                Console.WriteLine(field.Identifier + "(object):");
            else
                Console.WriteLine("  " + field.Identifier + ":" + field.Value.ToString());
            /* Output
                (object):
                    Name:Falcon
                    Message:Hi there
                Speed(object):
                    mph:18000
                Payload(array):
                    0:car
                    1:GPS unit
            */
        }

        // World-Schema DEFINITIONS EXPLICIT TAGS ::= 
        // BEGIN
        //  Rocket ::= SEQUENCE
        //  {
        //      name    [10] UTF8String(SIZE(1..16)),
        //      message [11] UTF8String DEFAULT "Hello World" , 
        //      fuel    [12] ENUMERATED { solid, liquid, gas, hybrid},
        //      speed   [13] CHOICE
        //      {
        //         mph  [20] INTEGER,  
        //         kmph [21] INTEGER
        //      }  OPTIONAL, 
        //      payload [14] SEQUENCE OF UTF8String
        //   }
        // END
		using System.IO;
        // The above schema was compiled into a DKey which is loaded into DCodec at runtime. See https://asn1.io/dynamic/DKey.aspx  
        using (Stream sf = File.OpenRead("myschema.dkey"))
        {
            var mySchema = new Oss.DCodec.DSchema(sf);

            var inType = mySchema.FindType("World-Schema.Rocket");

            var inMsg = "{'Name':'Falcon', 'Fuel':'solid', 'Payload':['none']}";

            var myCodec = new Oss.DCodec.JsonCodec();

            var outData = myCodec.Decode(inMsg, inType);

            Console.WriteLine("Name=" + outData["Name"]);

			// Message is absent in JSON, so the default is taken from the schema
            if (outData.ContainsKey("Message"))
                Console.WriteLine("Message=" + outData["Message"]);
            Console.WriteLine("Fuel=" + outData["Fuel"]);
                
			// Speed is absent in JSON, otherwise it'd be printed as ={key:value}, where key is either "mph" or "kmph"
            if (outData.ContainsKey("Speed"))
                Console.WriteLine("Speed=" + outData["Speed"]);
            Console.WriteLine("Payload=" + outData["Payload"]);
            /* Out:
                *  Name=Falcon
                *  Message=Hello World
                *  Fuel=solid
                *  Payload=["none"]
                */
        }
    // This example shows how to use a schema to validate messages, i.e. ensure
    // that the message contains all required fields and meets the constraints (if any).
    // Note, the example is in JSON, but DER messages will work the same way. 
    // 
    // DCodec also allows dynamic scenarios, where the schema and a message match
    // partially (see dynamic scenarios, e.g. schema evolution and partial decoding)
    // 
    // MySchema DEFINITIONS AUTOMATIC TAGS::= BEGIN  
    //   Rocket ::= SEQUENCE                            
    //   {                                              
    //      name        UTF8String (SIZE (4..16)),                 
    //      speed       INTEGER (0..1000),                    
    //      weight      REAL (0..1000) OPTIONAL,                       
    //      units       ENUMERATED { metric, imperial }
    //   }                                              
    // END                                              
            // Load a schema, then decode and validate messages (some valid, some not)
            var myCodec = new Oss.DCodec.JsonCodec(); 
			// load the above schema (use https://asn1.io/dynamic/DKey.aspx to get the schema key)
            var mySchema = new Oss.DCodec.DSchema("--- use https://asn1.io/dynamic/DKey.aspx to generate and paste the above schema key here ---");
            var myType = mySchema.FindType("MySchema.Rocket");

        // a valid message
        var goodMsg = "{'name':'Falcon', 'speed':0, 'units':'metric'}";
        // invalid messages
        var badMsg1 = "{'name':'Falcon'                             }"; // missing required fields
        var badMsg2 = "{'name':'xxx',    'speed':0, 'units':'metric'}"; // too short name 
        var badMsg3 = "{'name':'Falcon', 'speed':0, 'units':'knots' }"; // unknown units 

        // decode and validate
        myCodec.DecoderOptions.ValidateConstraints = true;
        var outData = myCodec.Decode(goodMsg, myType);

        try
        {
            outData = myCodec.Decode(badMsg1, myType);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            // 0808E: The DCodec operation encountered an unhandled conflict.. SchemaExtra: MySchema.Rocket.speed (missing required field from message)
        }
        try
        {
            outData = myCodec.Decode(badMsg2, myType);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            // 0808E: The DCodec operation encountered an unhandled conflict.. ConstraintViolation: MySchema.Rocket.name value=xxx (constraint: MySchema.Rocket(SIZE(4..16)); value size: 3)
        }
        try
        {
            outData = myCodec.Decode(badMsg3, myType);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            // 0808E: The DCodec operation encountered an unhandled conflict..SchemaMissing: MySchema.Rocket.units.knots value = knots(identifier not found in schema type)
        }
    // This example shows how to work with DValue - a dynamic type to hold your data.
    // Data objects are often called bindings, since they bind fields of your message with your code. 
    // Data objects can be either pre-defined at compile time (POCO) or created dynamically at runtime. 
    // There is trade-off between them, here is how they compare:
    //                    
    //                            IntelliSense       Strong typing        Access syntax
    // User-defined/POCO          full               yes                  obj.Prop
    // Dynamic/DValue/C# dynamic  none               no                   obj["Prop"] (C# dynamic obj.Prop)
	using System.Linq;
	using System.Collections.Generic;

	public class Fruit
    {
        public string Name { get; set; }
        public int Quantity { get; set; }
    }
                                            
			var myCodec = new Oss.DCodec.JsonCodec();

            // various ways to create and init DValues 
            var minotaur = Oss.DCodec.DValue.CreateObject().Add("name", "Minotaur"); // Add(key,value) 
            var atlas = new Oss.DCodec.DValueObject();
            atlas["name"] = "Atlas"; // ["key"] = value
            var delta = new Oss.DCodec.DValueObject { {"name", "Delta"} }; // object initializer 
            var falcon9 = new Oss.DCodec.DValueObject // object initializer 
            {
                { "name", "Falcon 9" },
                { "speed", new Oss.DCodec.DValueChoice("kmph", 50000) },
                {
                    "payload", new Oss.DCodec.DValueArray
                    {
                        "Car",
                        "GPS"
                    }
                }
            };
            dynamic taurus = new Oss.DCodec.DValueObject(); // C# dynamic wraps a DValue
            taurus.name = "Taurus"; // C# dynamic allows dot notation
            dynamic athena = Oss.DCodec.DValue.CreateObject();
            athena.name = "Athena";
            var titan = (Oss.DCodec.DValueObject) myCodec.Decode("{ 'Name':'Titan' }"); // Initializing with a JSON string 

            // Let's encode and print 
            var rockets = new List<Oss.DCodec.DValue>() { falcon9, atlas, minotaur, delta, taurus, titan, athena };
            foreach (var r in rockets)
                Console.WriteLine(myCodec.Encode(r));
            /* output:
                {"name":"Falcon 9","speed":{"kmph":50000},"payload":["Car","GPS"]}
                {"name":"Atlas"}
                {"name":"Minotaur"}
                {"name":"Delta"}
                {"name":"Taurus"}
                {"Name":"Titan"}
                {"name":"Athena"}
                */
                                            
            // C# dynamic type works well with JSON, when JSON keys contain no special characters (so keys are used for property names)
            dynamic rocket = myCodec.Decode("{ 'Name':'Falcon', 'Message':'Hi there', 'Speed': { 'mph':18000 }, 'Payload': [ 'car', 'GPS unit' ] }");
            Console.WriteLine("Name: " + rocket.Name);
            Console.WriteLine("Message: " + rocket["Message"]); // avoid special chars in a key
            Console.WriteLine("Speed: " + rocket.Speed.mph);
            foreach (var p in rocket.Payload)
                Console.WriteLine("Payload: " + p);
            Console.WriteLine("Weight: " + (rocket.Weight ?? "??"));
            /* output:
                Name: Falcon
                Message: Hi there
                Speed: 18000
                Payload: car
                Payload: GPS unit
                Weight: ??
            */
                                            
            // creating and encoding a DValue is as easy
            dynamic myRocket = new Oss.DCodec.DValueObject();
            myRocket.Name = "Space";
            myRocket.Message = "Hello ET";
            myRocket.Speed = new Oss.DCodec.DValueChoice("mph", 1000);
            myRocket.Payload = new Oss.DCodec.DValueArray("My car"); 
            myRocket.Payload.Add("My Radio");
            string msg = myCodec.Encode(myRocket);
            Console.WriteLine(msg);
            /* output: 
             *   {"Name":"Space","Message":"Hello ET","Speed":{"mph":1000},"Payload":["My car","My Radio"]}            
             */
                                            
            // DValue allows to explore dynamic data (e.g. to find unexpected or optional fields)
            var falcon = (Oss.DCodec.DValueObject) myCodec.Decode(@"{ 
                        'Name':'Falcon', 
                        'Message':'Hi there', 
                        'Speed': { 'mph':18000 }, 
                        'Payload': [ 'car', 'GPS unit' ], 
                        'Weight': 151.419 }");

        // enumerate children/traverse the data tree
        foreach (var elem in falcon.Iterator.Descendants)
            Console.WriteLine($"1: {elem.Indent}{elem.Identifier}:{elem.Value}");
        /* Output:
            1:   Name:Falcon
            1:   Message:Hi there
            1:   Speed:{"mph":18000}
            1:     mph:18000
            1:   Payload:["car","GPS unit"]
            1:     0:car
            1:     1:GPS unit
            1:   Weight:151.419
        */

        // enumerate generic collection 
        foreach (var field in falcon) 
            Console.WriteLine($"2:  {field.Key}:{field.Value}");
        /* Output:
            2:  Name:Falcon
            2:  Message:Hi there
            2:  Speed:{"mph":18000}
            2:  Payload:["car","GPS unit"]
            2:  Weight:151.419
        */

        // check for a field presence
        if (falcon.ContainsKey("Weight"))
            Console.WriteLine("found Weight: " + falcon["Weight"]);
        else
            Console.WriteLine("not found Weight");
        /* Output:
            found Weight: 151.419
        */            

        // find decimal fields via LINQ (note, it's bound to C# double by default)
        var numbers = from field in falcon.Iterator.Descendants
                        where field.Value.DataType == Oss.DCodec.DDataType.Double  
                        select field.Identifier;
        Console.WriteLine("Decimal fields:");
        foreach (var id in numbers)
            Console.WriteLine("  " + id);
        /* Output:
            Decimal fields:
                Weight
        */

                                        
        // use a DValue as an intermediate object to customize serialization of a POCO
        // Let's say we have a POCO Fruit 
		//		public class Fruit
		//		{
		//			public string Name { get; set; }
		//			public int Quantity { get; set; }
		//		}

        var apple  = new Fruit { Name = "Apple", Quantity = 100 };
        var orange = new Fruit { Name = "Orange", Quantity = 200 };
        var fruits = new List<Fruit>() { apple, orange};
        // fruits will be serialized as [{ "Name":"Apple", "Quantity":100},{ "Name":"Orange", "Quantity":200}] 
        // but we prefer a shorter form [{ "Apple":100 }, { "Orange":200}]
        // let's do it dynamically via a intermediate DValueArray 
        var fruitesTmp = new Oss.DCodec.DValueArray(fruits.Select(item => new Oss.DCodec.DValueObject() { { item.Name, item.Quantity } }));
        string jsonArray = myCodec.Encode(fruitesTmp);
        Console.WriteLine(jsonArray);
        /* Output
            *  [{"Apple":100},{"Orange":200}]
            */

                                        
        // DValueChoice - one of many, e.g. either kmph or mph
        dynamic speed = new Oss.DCodec.DValueChoice("kmph", 28968);
        Console.WriteLine(myCodec.Encode(speed));
        speed.mph = 18000; // switch to miles per hour
        Console.WriteLine(myCodec.Encode(speed));
        /* Output
            *  {"kmph":28968}
            *  {"mph":18000}
            */

                                        
    // This example shows how to work with pre-defined bindings (user defined data objects), 
    // aka POCO https://en.wikipedia.org/wiki/Plain_old_CLR_object
	using System.Collections.Generic;

    // Define our data to be encoded/decoded
    public class Rocket
    {
        public string Name { get; set; }
        [Oss.DCodec.DField(CustomName = "Message")]  // DField attribute specifies a name to match JSON key
        public string Note { get; set; }
        public Speed Speed; // Binding for ASN.1 type CHOICE 
        public List<string> Payload { get; set; }
    }

    // let DCodec know that this is a special class bound to ASN.1 CHOICE. 
    // Choice of either kilometers per hour or miles per hour, but not both.
    [Oss.DCodec.DChoice]
    public class Speed 
    {
        public int? Kmph { get; set; }
        public int? Mph { get; set; }
    }
        // Encode/Decode a Rocket 
        var myCodec = new Oss.DCodec.JsonCodec();

        var NewRocket = new Rocket
        {
            Name = "Falcon Heavy",
            Note = "I'm here",
            Speed = new Speed
            {
                Mph = 18000
            }
        };

        myCodec.EncoderOptions.Formatting = Oss.DCodec.JsonFormatting.Indented;
        string msg = myCodec.Encode(NewRocket);
        Console.WriteLine(msg);
        /* output:
                {
                "Name":"Falcon Heavy",
                "Message":"I'm here",
                "Speed":{
                "Mph":18000
                }
            }
        */

        // Decode a Rocket
        var jsonMsg = @"{ 
                        'Name':'Falcon', 
                        'Message':'Hi there', 
                        'Speed': { 'Mph':18000 }, 
                        'Payload': [ 'GPS unit', 'Car' ]
                        }";
        var rocket = myCodec.Decode<Rocket>(jsonMsg);

        Console.WriteLine("Name: " + rocket.Name);
        Console.WriteLine("Message: " + rocket.Note);

        if (rocket.Speed.Mph != null)
            Console.WriteLine("Speed (mph): " + rocket.Speed.Mph);
        else if (rocket.Speed.Kmph != null)
            Console.WriteLine("Speed (kmph): " + rocket.Speed.Kmph);
        else
            Console.WriteLine("Unknown speed units");

        Console.WriteLine("Payload: " + string.Join(",", rocket.Payload.ToArray()));
        /* output:
            Name: Falcon
            Message: Hi there
            Speed (mph):18000
            Payload: GPS unit,Car
        */
        // encode/decode useful/native C# types: decimal/double, List, Dictionary  
        var real64 = new List<double> { 3.14D, double.NegativeInfinity, double.PositiveInfinity, double.NaN };
        var realDec = new List<decimal> { 3.14M, decimal.MinusOne, decimal.One, decimal.Zero, decimal.MinValue, decimal.MaxValue };
        myCodec.EncoderOptions.Formatting = Oss.DCodec.JsonFormatting.None;
        Console.WriteLine(myCodec.Encode(real64));
        Console.WriteLine(myCodec.Encode(realDec));
        /* Output:
            [3.1400000000000001,"-INF","INF","NaN"]
            [3.14,-1.0,1.0,0.0,-79228162514264337593543950335.0,79228162514264337593543950335.0]
        */
        var dict = myCodec.Decode<Dictionary<string, object >>("{'Name':'Falcon', 'Message':'Hi there', 'Speed': { 'mph':18000 }, 'Payload': [ 'car', 'GPS unit' ] }");
        Console.WriteLine(myCodec.Encode(dict));
        /* Output:
            *   {"Name":"Falcon","Message":"Hi there","Speed":{"mph":18000},"Payload":["car","GPS unit"]}
            */
            // convert DValue to/from POCO
			var dvRocket = new Oss.DCodec.DValueObject { {"name", "Delta"}, {"Payload", new Oss.DCodec.DValueArray { "Car", "GPS" } } };
            var pocoRocket = dvRocket.ToObject<Rocket>();
            dvRocket = Oss.DCodec.DValue.FromObject(pocoRocket) as Oss.DCodec.DValueObject;

    // Usually the schema, the message, and the bindings match each other. E.g. a Rocket has 
    // a Speed property which is a number, then Speed must appear in the user defined bindings, 
    // in the schema, and in the message, or otherwise something is wrong. 
    // However the data change/evolve all the time, sometime unexpected, which cause 
    // "conflicts" during serialization/validation. There are different kinds of Conflicts, 
    // and mostly during Decode, since the incoming data are more likely to deviate. 
    // With DCodec the app can handle conflicts and recover from them (e.g. to be 
    // "liberal on receiving") without interrupting the operation. The app just have to provide 
    // a conflict handler, because otherwise DCodec throws an exception.
    //
    // Lets define three sets of data for a Rocket: the schema type, the binding class, and a JSON message
    // each slightly different from the other two and see which conflicts the app receives 
    // during serialization and validation.

        // Schema
        // World-Schema-C DEFINITIONS EXPLICIT TAGS::= BEGIN         
        //   Rocket ::= SEQUENCE                                     
        //   {                                                     
        //      name        [1] UTF8String,                       
        //      speed       [2] INTEGER (1..100000),                       
        //      weight      [3] REAL,                       
        //      units       [7] ENUMERATED { metric, royal },
        // 	    payload     [8] SEQUENCE SIZE(10) OF UTF8String OPTIONAL,      
        // 	    oneway      [9] BOOLEAN OPTIONAL      
        //   }
        // END

        // Pre-defined bindings (POCO)
        public class Rocket4
        {
            public string Name { get; set; }
            public string Date { get; set; }
            public int Speed { get; set; }
            public int Stages { get; set; }
            public U Units {get; set;}
            public enum U { metric, royal }
            public bool? Oneway { get; set; } // OPTIONAL
        }

        public static Oss.DCodec.DResponse ConflictsHandlerPrintAndContinue(Oss.DCodec.DConflict conflict)
        {
            System.Console.WriteLine(conflict);
            return Oss.DCodec.DResponse.Continue;
        }
        var myCodec = new Oss.DCodec.JsonCodec();
        // we'll use default conflicts/validation options
        // myCodec.DecoderOptions.ValidateConstraints = true; // true is default
        // myCodec.DecoderOptions.ConflictsValue = Oss.DCodec.DConflictsValue.Parse;
        // Schema-less decoding fires a few conflicts. For each field let's see if there is a conflict: 
        //  Bindings            Message
        //  string Name        'Name':'My rocket' -- no conflict, name and type match
        //  string Date        'Date':2018        -- no conflict, number is converted to string
        //  int Speed          'Speed':null       -- no conflict, null is converted to 0
        //  int Stages         'Stages':'ABC'     -- BindingTypeMismatch, string cannot be converted into int
        //    --               'Weight':250000    -- BindingMissing, binding is missing this field
        //  enum Units           --               -- no conflict, extra fields in the bindings are not a problem
        //                     'Name':'Rocket'    -- DuplicatedKey, Name was encountered twice
        //    --                  --              -- no conflict, Payload is only in the schema, but we don't use schema yet
        //  bool? Oneway        'Oneway':null     -- no conflict, binding is nullable, hence optional

        string msg=@"{
                        'Name' : 'My rocket' ,
                        'Date' : 2018,
                        'Speed' : null,
                        'Stages' : 'ABC' ,
                        'Weight' : 250000,
                        'Name' : 'Rocket' ,
                        'Oneway' : null
                    }";
        myCodec.DecoderOptions.ConflictHandler = ConflictsHandlerPrintAndContinue;
        var rocket = myCodec.Decode<Rocket4>(msg); // a liberal handler: c => {Log(); return DResponse.Continue;}
        /* Output
            BindingTypeMismatch: Stages value=ABC
            BindingMissing: Weight value=250000
            DuplicatedKey: Name value=Rocket
        */

        // Schema-less encode fire no conflicts, since the message just follows the bindings.
        var encoding = myCodec.Encode(rocket);
        /* No Output, there is no conflicts
        */
        // Using a schema bring more potential conflicts than schema-less decoding (schema key generated @ https://asn1.io/dynamic)
        var mySchema = new Oss.DCodec.DSchema("...");

        //  Bindings      Schema           Message
        //  string Name   name   UTF8     'Name'  : 'Fake rocket' -- no conflicts
        //  string Date     --            'Date'  : 2018,         -- SchemaMissing (schema has no Date) but no type mismatch (number is converted)
        //  int Speed     speed  INT      'Speed' : null,         -- SchemaTypeMismatch, BindingTypeMismatch, null cannot be used for a required field
        //  int Stages     --             'Stages': 'ABC',        -- BindingTypeMismatch, SchemaMissing
        //    --          weight REAL     'Weight': true,         -- BindingMissing, SchemaTypeMismatch (REAL vs boolean).
        //  enum Units    units  ENUM       --                    -- SchemaExtra, units is expected in the message
        //                                'Name'  : 'Test rocket' -- DuplicatedKey
        //    --         payload INT OPT      --                  -- no conflicts, Payload is OPTIONAL
        //  bool Oneway   oneway BOOL     'Oneway':null           -- no conflicts, OPTIONAL, null in the message, no conflicts

        // Message
        msg = @"{ 
                    'Name'  : 'Fake rocket',
                    'Date'  : 2018,
                    'Speed' : null,
                    'Stages': 'ABC', 
                    'Weight': true,
                    'Name'  : 'Test rocket',
                    'Oneway': null
                }";
            
        rocket = myCodec.Decode<Rocket4>(msg, mySchema.FindType("World-Schema-C.Rocket"));
        /* Output
            SchemaMissing: World-Schema-C.Rocket.Date value=2018 
            SchemaTypeMismatch: World-Schema-C.Rocket.speed 
            BindingTypeMismatch: World-Schema-C.Rocket.speed 
            BindingTypeMismatch: World-Schema-C.Rocket.Stages value=ABC 
            SchemaMissing: World-Schema-C.Rocket.Stages value=0 
            SchemaTypeMismatch: World-Schema-C.Rocket.weight value=True 
            BindingMissing: World-Schema-C.Rocket.weight value=True 
            DuplicatedKey: World-Schema-C.Rocket.name value=Test rocket
            SchemaExtra: World-Schema-C.Rocket.units 
        */

        Console.WriteLine("-------------------- Conflicts of decoding dynamic type --------------------");
        // for a dynamic type, the output object is created dynamically, so there is no binding conflicts
        var dvRocket = myCodec.Decode(msg, mySchema.FindType("World-Schema-C.Rocket"));
        /*
            SchemaMissing: World-Schema.Rocket.Date value=2018
            SchemaTypeMismatch: World-Schema.Rocket.speed 
            SchemaMissing: World-Schema.Rocket.Stages value=ABC
            SchemaTypeMismatch: World-Schema.Rocket.weight value=True
            DuplicatedKey: World-Schema.Rocket.name value=Test rocket
            SchemaExtra: World-Schema.Rocket.units
        */

        // mismatched ENUM or CHOICE items also result in conflicts 
        msg = @"{ 'Name':'Alpha','Speed' : 1, 'Units':'British'}";  // "British" is not in the schema 
        dvRocket = myCodec.Decode(msg, mySchema.FindType("World-Schema-C.Rocket"));
        /*
            SchemaMissing: World-Schema-C.Rocket.units.British value=British
            SchemaExtra: World-Schema-C.Rocket.weight            
        */

        dvRocket["Units"] = "British"; //  "British" is not in the schema
        myCodec.EncoderOptions.ConflictHandler = ConflictsHandlerPrintAndContinue;
        msg = myCodec.Encode(dvRocket, mySchema.FindType("World-Schema-C.Rocket"));
        /*
            SchemaExtra: World-Schema-C.Rocket.weight 
            SchemaMissing: World-Schema-C.Rocket.units.British value=British 
        */
        // Validating and enumerating the conflicts
        dvRocket = new Oss.DCodec.DValueObject
        {
            { "Name", "Delta" },
            { "Speed", -1 },        // invalid, must be 1..100000
            { "Units", "metric" }   
            // { "Weight", 100.0 }  // invalid, is required
        };
        var problems = myCodec.Validate(dvRocket, mySchema.FindType("World-Schema-C.Rocket"));
        foreach (var p in problems)
            Console.WriteLine(p);
        /* output:
            ConstraintViolation: World-Schema-C.Rocket.speed value=-1 
            SchemaExtra: World-Schema-C.Rocket.weight
            */
    // This example shows how to do а partial decoding, e.g. when the application  
    // needs only a few fields (out of a big message) and the rest can be ignored. 
    // Let's consider this schema as "big", with "lots" of fields, while the app 
    // needs only Rocket.Speed to perform its logic. Also note, that the schema
    // can be extended later by adding fields in Rocket.Other.
    // ===============================================         
    // World-Schema DEFINITIONS EXPLICIT TAGS::= BEGIN         
    //   Rocket ::= SEQUENCE                                     
    //   {                                                     
    //      name        [1] UTF8String,                       
    //      speed       [2] INTEGER,                       
    //      other       [3] SEQUENCE { ... } -- extensible
    //   }                                                     
    // END                                                     
    // ===============================================                                                                

	using Oss.DCodec;

    // Data type to decode into (bindings)
    public class Essentials
    {
        // not interested in 
        //   public string Name { get; set; }
        //   public Stuff other { get; set; }

		// interested in
        public int Speed { get; set; }
    }
        // Partial decoding - when only parts of the message are decoded
        var myCodec = new JsonCodec();

        // Load the schema (dkey is generated with https://asn1.io/dynamic)
        var mySchema = new DSchema("....dkey....");

        // message includes lots of stuff, but only Speed is essential for this app
        string msg = "{ 'Name':'Gemini', 'Speed':45000,  'Other':{'id':'x1234', 'size':20, 'Payload':true, 'Launches':[1964,1965,1966]} }";

        // bindings and the message partially match the schema, so conflicts are expected (and handled).
        myCodec.DecoderOptions.ConflictHandler = MyHandler;
        var rocket = myCodec.Decode<Essentials>(msg,  mySchema.FindType("World-Schema.Rocket"));

        if (rocket.Speed > 40000)
            Console.WriteLine("Orbiting...");
        // Output:
        //    New field:   World-Schema.Rocket.other.id
        //    New field:   World-Schema.Rocket.other.size
        //    New field:   World-Schema.Rocket.other.Payload
        //    New field:   World-Schema.Rocket.other.Launches
        //    Orbiting...
    public DResponse MyHandler(DConflict conflict)
    {
        switch (conflict.Kind)
        {
            // Ignore fields that are not essential
            case DConflictKind.BindingMissing:
                return DResponse.Drop;

            // Log fields that are new (do not match the schema)
            case DConflictKind.SchemaMissing:
                System.Console.WriteLine("New field:   " + conflict.Reference); 
                return DResponse.Continue;

            // Break on any other kind of conflict. DOperationInterupted will be thrown.
            default:
                return DResponse.Break; 
        }
    }

    // This example shows how to handle messages that come from nodes that use 
    // a newer (evolved) version of a schema. In a "classic" decoding scenario
    // the whole message would be rejected (as not valid). In a "dynamic" scenario 
    // the decoder will parse and return both, the valid (matching) part and the 
    // invalid (conflicting) part of the message. The application then handles 
    // the conflicted part (e.g. mine for data and decides what to do about 
    // the whole message. 
    //
    // Here are three schemas side-by-side: the original, the evolved with 
    // a new field, and another evolved with a field type change
    //
    // The original schema (used by this app)      Evolved Schema (used by a remote node)    Evolved yet again 
    // -----------------------------------------   --------------------------------------    -----------------------------------------------------------
    //  World-Schema DEFINITIONS ::= BEGIN         World-Schema DEFINITIONS ::= BEGIN        World-Schema DEFINITIONS ::= BEGIN
    //   Rocket ::= SEQUENCE                        Rocket ::= SEQUENCE                       Rocket ::= SEQUENCE
    //   {                                          {                                         {
    //     name    [1] UTF8String,                     name    [1] UTF8String,                   name    [1] UTF8String,             
    //     payload [2] BOOLEAN                         payload [2] BOOLEAN,                      payload [2] SEQUENCE OF UTF8String, -- change in ver.3.0
    //                                                 weight  [3] REAL  -- new in ver 2.0       weight  [3] REAL 
    //   }                                          }                                         }
    //  END                                        END                                       END
		public static void Main()
		{
            // Schema evolution 
            // schema key (generated at https://asn1.io/dynamic)
            var mySchema = new Oss.DCodec.DSchema(....);
            var myType = mySchema.FindType("World-Schema.Rocket");

        string msgVer1 = "{ 'Name' : 'My rocket', 'Payload' : true}";
        string msgVer2 = "{ 'Name' : 'My rocket', 'Payload' : true, 'Weight' : 10000.0 }";
        string msgVer3 = "{ 'Name' : 'My rocket', 'Payload' : ['Car'], 'Weight' : 11000.0 }";

        var myCodec = new Oss.DCodec.JsonCodec();
        myCodec.DecoderOptions.ConflictHandler = EvolutionHandler;
        var myRocket = myCodec.Decode(msgVer1, myType); 
        // Output: no conflicts ...

        var myRocketIsOlder = myCodec.Decode(msgVer2, myType);
        // Output:
        //    SchemaMissing: World-Schema.Rocket.Weight value=10000 (identifier not found in schema type)

        var myRocketIsOldest = myCodec.Decode(msgVer3, myType);
        // Output:
        //    SchemaTypeMismatch: World-Schema.Rocket.payload value=["Car"] (invalid format for a boolean value)
        //    Mine for patterns: ["Car"]
        //    SchemaMissing: World-Schema.Rocket.Weight value=11000 (identifier not found in schema type)            
        }

        public static Oss.DCodec.DResponse EvolutionHandler(Oss.DCodec.DConflict conflict)
        {
            Console.WriteLine(conflict);
            switch (conflict.Kind)
            {
                // we allow schema evolution e.g. the following conflicts are ok
                case Oss.DCodec.DConflictKind.SchemaMissing:
                    return Oss.DCodec.DResponse.Continue;

            case Oss.DCodec.DConflictKind.SchemaTypeMismatch:
                Console.WriteLine("Mine for patterns: " + (conflict.Value as Oss.DCodec.DValue).ToString());
                return Oss.DCodec.DResponse.Continue;

            // any other conflicts are unexpected, so the message is invalid
            default:
                return Oss.DCodec.DResponse.Break; // codec will throw DOperationInterupted
        }
    }

    // This example shows how to decode DER messages for which there is no schema 
    // (or no schema for parts of the message). The application gets the no-schema 
    // part in a form of raw values, aka TLV (Tag/Length/Value), then use some 
    // information, e.g check types (when available), find string pattern, etc.
    // NOTE: field types won't be known unless explicit tags are used
    // ==========================================================================
    // World-Schema DEFINITIONS EXPLICIT TAGS ::=                    
    //  BEGIN
    //    Rocket ::= SEQUENCE
    //    {
    //      name    [10] UTF8String(SIZE(1..16)),               
    //      message [11] UTF8String DEFAULT "Hello World" ,      
    //      fuel    [12] ENUMERATED { solid, liquid, gas, hybrid},
    //      speed   [13] CHOICE { mph [20] INTEGER,  kmph [21] INTEGER }  OPTIONAL,
    //      payload [14] SEQUENCE OF UTF8String
    //    }
    //  END
    // ==========================================================================
        var myCodec = new Oss.DCodec.DerCodec();

            // message in DER (JSON equivalent { "name":"Falcon",  "message":"Hello World",  "fuel":"solid",  "speed":{ "mph":10000}, "payload":["Car","GPS" ] })
		byte[] msgDerRawExplicitTags = new byte[] {
                                                   0x30, 0x25,
                                                        0xAA, 0x08,
                                                          0x0C, 0x06, 0x46, 0x61, 0x6C, 0x63, 0x6F, 0x6E,
                                                        0xAC, 0x03,
                                                          0x0A, 0x01, 0x00,
                                                        0xAD, 0x06,
                                                          0xB4, 0x04,
                                                            0x02, 0x02, 0x27, 0x10,
                                                        0xAE, 0x0C,
                                                          0x30, 0x0A,
                                                            0x0C, 0x03, 0x43, 0x61, 0x72,
                                                            0x0C, 0x03, 0x47, 0x50, 0x53};

        var stream = new System.IO.MemoryStream(msgDerRawExplicitTags);

        var tlvs = myCodec.Decode(stream);

        Console.WriteLine("Explore/iterate:");
        foreach (var elem in tlvs.Iterator.Descendants)
            Console.WriteLine($" {elem.Indent}{elem.Index:X}:[{elem.Identifier}] {elem.Value.DataType}, {elem.Value.Data}");

        Console.WriteLine("Access by index:");
        Console.WriteLine($" name = {tlvs[0]}");         // ordered object (SEQUENCE)
        Console.WriteLine($" speed.mph = {tlvs[2][0]}"); // nested (SEQUENCE or CHOICE)
        Console.WriteLine($" payload[0] = {tlvs[3][0]}");// array (SEQUENCE OF)

        Console.WriteLine("Access by tag:");
        Console.WriteLine($" name = {tlvs["10"]}");
        Console.WriteLine($" speed.mph {tlvs["13"]["20"]}");
        Console.WriteLine($" payload[0] = {tlvs["14"][0]}"); 

        if (!tlvs.ContainsKey("42")) // doesn't exist
            Console.WriteLine(" Field not found");
        /* output: 
            Explore/iterate:
                0:[10] String, Falcon
                1:[12] Int, 0
                2:[13] Raw,
                0:[20] Int, 10000
                3:[14] Raw,
                0:[0] String, Car
                1:[1] String, GPS
            Access by index:
                name = Falcon
                speed.mph = 10000
                payload[0] = Car
            Access by tag:
                name = Falcon
                speed.mph 10000
                payload[0] = Car
                Field not found
        */
    // This example shows DER encoding/decoding. 
    // DCodec is strict on encoding (the encoding must be valid)
    // and is liberal on decoding (allows partial schema match).
    //
    // Use of EXPLICIT TAGS mode ensures that DER tags will include for both, 
    // field identification and type, which allows greater flexibility 
    // (e.g. schema evolution, partial decoding, making sense of fields 
    // that do not exists in the schema. 
    //
    // MySchema DEFINITIONS EXPLICIT TAGS::= BEGIN  
    //   Rocket ::= SEQUENCE                            
    //   {                                              
    //      name    [1] UTF8String (SIZE (4..16)) DEFAULT "Falcon", 
    //      speed   [2] INTEGER (0..1000),                    
    //      weight  [3] REAL (0..1000) OPTIONAL,                       
    //      units   [4] ENUMERATED { metric, imperial }
    //   }                                              
    // END                                 
    using Oss.DCodec;             
            // Encode and Decode DER
            var myCodec = new DerCodec();
            var jsonCodec = new JsonCodec(); // for debugging in JSON
            // schema types
            var mySchema = new DSchema("Zb5qQllrUFRhNbCmGA22SiHkvVOZhXLQoJYwCNrHkvKylA9JgN8oiFX4xNgnlvkTFEGhL4tUfFZxdTT2Dy9Vorrko8pSuniCZQUP416wH8lQAPAu+1rz+Zdxrnzp3GHPaHYmqyWXHPAO3XhPrYHJl4B6b9Hanz+2g3kSyjtJuxNd9Qy+fjx4/5uhXo4EJ2gVGd4MM0XypcNKEkV34BhDE7oDtOXfmtM1sJWf5RjJoxVT8LxR+E8N5sUKPwspESsZiSAKmEX8AB/RD0IBNXYi/fQ9ZPZvCVl87Mpi1GYVSZE667Ru/ds1RpLD0KGgpfpBGQSLGMSJrtz5RmCQg8KFvYOrE7xOxyhjZGXuN3JN4nUtZPB/cCDcbfB33c0enKrpgFPuRKch0zxuAVIvR52RdIw3WvcLdKtQh5AUg41cM1ThleklHkuX/apJ8XZLCgh/F+HpGWC5X7KN1BuYRiWR8E89wBGM/7dww9UKKlptgvbdGkhIVH23ZXDvDhZhXWfYucsyIY1pMp9F5Huoq7UH2SN79xWB+IgnIqTyBthukW6qEnDLcMzI0EV/A3vO3shXO27IJjNgRmTtooNq7VZVhabYPrLp+SucBA+DEyr3gxx7SWvRm68lbF0zCsoGnlu0lqT8OpbQDLAU6eErMkmhjDOvLVoY60Ua7l7NuHVADWugsCc7uTskkPEHzQuLKi6ZionfVPV61I6hCloiT9Z4kyzRRQwCNj3OCOKZYJyqmbifjk6bN9XPumnABZ3vQWmoym41GN8kFRWfBHW9xgmgOrKrVohLLI9BmXeGS2ceDRbRS0fl8t5oioB0IDtPddPIFcjtTBv++Nf6PK4T++iI6DISbz/R4lC7MbodrNzH4n8wedUn2eNW7IHuHhjyWgQdKvSqRtHYQDG4W/eFH/NSUw0WTiea9R4vy7VukAFMH5xoM7OWGsxmUFQ=");
            var myType = mySchema.FindType("MySchema.Rocket");

            var rocketOut = new DValueObject
            {
                //["name"] = "Falcon", // default value, no need to specify/encode
                ["speed"] = 0,
                ["weight"] = 100.5M, 
                ["units"] = "metric",
                ["payload"] = "mirror" // extra field, not in the schema, won't be encoded
            };

            var hasConflicts = false;
            myCodec.EncoderOptions.ConflictHandler = c => {
                hasConflicts = true;
                Console.WriteLine(c); // log the conflicts
                return DResponse.Drop; // Drop extra fields
            };
            var derRocket = myCodec.Encode(rocketOut, myType);
            if (hasConflicts) 
                Console.WriteLine("Encoding encountered some conflicts...");

            /* Output:
                SchemaMissing: MySchema.Rocket.payload (identifier not found in schema type)
                Encoding encountered some conflicts...
            */

            // print DER
            Console.WriteLine("HEX:  " + BitConverter.ToString(derRocket));
            Console.WriteLine("JSON: " + jsonCodec.Encode(myCodec.Decode(derRocket, myType)));
            /* Output:
             *  HEX:  30-17-A2-03-02-01-00-A3-0B-09-09-03-31-30-30-35-2E-45-2D-31-A4-03-0A-01-00
             *  JSON: {"speed":0,"weight":100.5,"units":"metric","name":"Falcon"}
             */

            // a message that contains an extra field 
            byte[] derRocketExtra = new byte[] { 0x30, 0x21, 0xA2, 0x03, 0x02, 0x01, 0x00, 0xA3, 0x0B, 0x09, 0x09, 0x03, 0x31, 0x30, 0x30, 0x35, 0x2E, 0x45, 0x2D, 0x31, 0xA4, 0x03, 0x0A, 0x01, 0x00, 0xA5, 0x08, 0x0C, 0x06, 0x6D, 0x69, 0x72, 0x72, 0x6F, 0x72 };
            myCodec.DecoderOptions.ConflictHandler = c => {
                Console.WriteLine(c); // log the conflicts
                return DResponse.Continue; // allow extra fields
            };
            var rocketIn = myCodec.Decode(derRocketExtra, myType);

            Console.WriteLine("JSON: " + jsonCodec.Encode(rocketIn));
            /* Output:
                SchemaMissing: MySchema.Rocket.[5] value=mirror (a field with DER tag [5] was not found in schema type)
                JSON: {"speed":0,"weight":100.5,"units":"metric","5":"mirror","name":"Falcon"}
            */