The C# class variables corresponding to each of the ASN.1 types and method of population are the same as they were in the BER encoding case. See the section Populating Generated Variables for Encoding for instructions on how to populate the variables prior to encoding.
Once an object's member variables have been populated, the object's encode method can be invoked to encode the value. The general procedure to do this involves the following three steps:
Create an encode message buffer or output stream object into which the value will be encoded
Invoke encode methods. These include the EncodeStartDocument and EncodeEndDocument methods from the Asn1XerEncodeBuffer class and the encode method from the ASN1C generated class.
If the encode message buffer is used: invoke encode message buffer methods to access the encoded message component. If the output stream is used: close the stream.
The first step is the creation of an encode message buffer object. For XER encoding, this is an object of the Asn1XerEncodeBuffer class. The following constructors are available for creating an XER encode buffer object:
public Asn1XerEncodeBuffer (); public Asn1XerEncodeBuffer (bool canonical, int sizeIncrement);
The default constructor sets all internal buffer control variables to default values. Canonical XER is set to false and size increment is set to 1024. The other forms of the constructor allow these variables to be changed. Canonical XER specifies that the canonical form of XER encoding (CXER as specified in X.693) should be used. Size increment specifies the amount by which the dynamic encode buffer should be expanded when it fills up. This should be set lower for small, memory-constrained environments and higher if large messages are being encoded.
If the output stream method is used then the first step is the creation of an output stream. For XER encoding, this is an object of the Asn1XerOutputStream class. The following constructors are available for creating an XER encode buffer object:
public Asn1XerOutputStream (OutputStream os); public Asn1XerOutputStream (OutputStream os, bool canonical, int bufSize);
The first constructor creates a buffered XER output stream with default size of an internal buffer. Canonical XER is set to false. The other form of the constructor allows these variables to be changed. Canonical XER specifies that the canonical form of XER encoding (CXER as specified in X.693) should be used. The buffer size argument specifies the size of the internal buffer of the stream. Larger buffer sizes typically provide better performance at the expense of increased memory consumption.
Similar classes exist for XML encode buffer and streams:
public Asn1XmlEncodeBuffer () public Asn1XmlEncodeBuffer (int sizeIncrement) public Asn1XmlOutputStream (OutputStream os) public Asn1XmlOutputStream (OutputStream os, int bufSize)
The main difference is the XML classes to not have a canonical XML option; therefore, there is not cxer or canonical boolean argument.
The second step is the invocation of the encode methods. The calling arguments were described earlier. As per the C# standard, this method must be invoked from within a try/catch block to catch the possible Asn1Exception or System.Exception that may be thrown. Alternatively, the method from which the encode method is called can declare that it throws Asn1Exception and System.Exception leaving it to be dealt with at a higher level.
Finally, if a message buffer is used, encode buffer methods can be called to access the encoded message component. The C# API provides an object called a ByteArrayInputStream that provides a way to look at the encoded component as a stream. The encode buffer object provides a method called GetInputStream that returns a byte array input stream representing the message component. This is the preferred way to access the encoded component.
In addition to GetInputStream, there is a MsgCopy property that will retrieve a copy of the generated message into a byte array object. This is somewhat slower because a copy needs to be done. Another option that is available when doing XER encoding is the Buffer property. This returns a reference to the actual message buffer into which the message was encoded. Since an XER message is encoded front-to-back (unlike the back-to-front used in BER/DER encoding), the buffer reference returned will point to the start of the encoded message. The MsgLength property can then be used to get the message length (in bytes). Note that the byte count may not correspond to the actual character count as UTF-8 encoding is used and some characters may be multiple bytes in length.
If an output stream is used, the stream should be closed when encoding is complete to ensure all buffered data is flushed to the output device.
The Asn1XerEncodeBuffer encode buffer class also contains other methods for operating directly on the encoded component (for example, the write method can be used to write it to a file or other medium). A user could also derive their own special encode buffer class from this class to add more functionality. See the description of the Asn1XerEncodeBuffer class in the run-time section for a full description of the available methods.
A complete example showing how to invoke an XER encode method is as follows:
// Note: personnelRecord object was previously populated with data // Step 1: Create a message buffer object. This object uses // standard XER (non-canonical) and the default size increment // for buffer expansion.. Asn1XerEncodeBuffer encodeBuffer = new Asn1XerEncodeBuffer(); // Step 2: Invoke the encode methods. These include // encodeStartDocument to encode the XML document header, // the generated C# encode method to encode the document body, // and the encodeEndDocument method to complete the message. // Note that these methods must be invoked from within a // try/catch block.. try { encodeBuffer.EncodeStartDocument (); personnelRecord.Encode (encodeBuffer, null); encodeBuffer.EncodeEndDocument (); if (trace) { System.Console.Out.WriteLine ("Encoding was successful"); encodeBuffer.Write (System.Console.OpenStandardOutput()); } // Step 3: Access the encoded message component. In this // case, we use methods in the class to write the encoded // XML document to a file.. encodeBuffer.Write(new System.IO.FileStream(filename, System.IO.FileMode.Create)); // We can also directly access the buffer as follows: byte[] buffer = encodeBuffer.Buffer; int msglen = encodeBuffer.MsgByteCnt; } catch (Exception e) { System.Console.Out.WriteLine (e.Message); Asn1Util.WriteStackTrace(e, Console.Error); return; }
An example showing stream-based encoding is as follows:
// Note: personnelRecord object was previously populated with data Asn1XerOutputSteram outs = null; try { // Step 1: Create an output stream object. This object // uses standard XER (non-canonical) and the default // internal buffer’s size. outs = new Asn1XerOutputStream(new System.IO.FileStream( filename, System.IO.FileMode.Create)); // Step 2: Invoke the encode methods. These include // encodeStartDocument to encode the XML document header, // the generated C# encode method to encode the document body, // and the encodeEndDocument method to complete the message. // Note that these methods must be invoked from within a // try/catch block.. outs.EncodeStartDocument (); personnelRecord.Encode (outs, null); outs.EncodeEndDocument (); if (trace) { System.Console.Out.WriteLine ("Encoding was successful"); encodeBuffer.Write (System.Console.OpenStandardOutput()); } } catch (Exception e) { System.Console.Out.WriteLine (e.Message); Asn1Util.WriteStackTrace(e, Console.Error); return; } finally { // Step 3: Close the stream. try { if (outs != null) outs.Close (); } catch (Exception e) {} }
If you compare these examples with the other encoding examples, you will see the procedures are similar. This makes it very easy to switch encoding methods should the need arise.
In the case of XML encode, the procedure is very similar. The only difference is that it is not necessary to call the EncodeStartDocument and EncodeEndDocument methods. The are built into the generated encode method for PDU data types.
The resulting XML document from running the program above is as follows:
<?xml version="1.0" encoding="UTF-8"?> <PersonnelRecord> <name> <givenName>John</givenName> <initial>P</initial> <familyName>Smith</familyName> </name> <number>51</number> <title>Director</title> <dateOfHire>19710917</dateOfHire> <nameOfSpouse> <givenName>Mary</givenName> <initial>T</initial> <familyName>Smith</familyName> </nameOfSpouse> <children> <ChildInformation> <name> <givenName>Ralph</givenName> <initial>T</initial> <familyName>Smith</familyName> </name> <dateOfBirth>19571111</dateOfBirth> </ChildInformation> <ChildInformation> <name> <givenName>Susan</givenName> <initial>B</initial> <familyName>Jones</familyName> </name> <dateOfBirth>19590717</dateOfBirth> </ChildInformation> </children> </PersonnelRecord>