RSCG – DotnetYang
Generating source code from YANG models
This is how you can use DotnetYang .
The code that you start with is
<project sdk="Microsoft.NET.Sdk">
<propertygroup>
<outputtype>Exe</outputtype>
<targetframework>net8.0</targetframework>
<implicitusings>enable</implicitusings>
<nullable>enable</nullable>
</propertygroup>
<itemgroup>
<additionalfiles include="demo.yang">
</additionalfiles>
<propertygroup>
<emitcompilergeneratedfiles>true</emitcompilergeneratedfiles>
<compilergeneratedfilesoutputpath>$(BaseIntermediateOutputPath)\GX</compilergeneratedfilesoutputpath>
</propertygroup>
<itemgroup>
<packagereference version="0.3.0" include="dotnetYang">
</packagereference>
</itemgroup>
The code that you will use is
Console.WriteLine("Yang file from https://info.support.huawei.com/info-finder/encyclopedia/en/YANG.html#content4!");
Some.Module.YangNode.DoSomethingInput input = new Some.Module.YangNode.DoSomethingInput
{
TheBigLeaf = 123
};
module some-module {
yang-version 1.1;
namespace "urn:dotnet:yang:andrei";
prefix sm;
identity someIdentity;
identity someOtherIdentity
{
base someIdentity;
}
rpc doSomething {
input {
leaf the-big-leaf
{
type uint32;
default "4";
description "The value that is the input of the doSomething rpc";
}
}
output {
leaf response
{
type identityref
{
base someIdentity;
}
default "someOtherIdentity";
description "The identity that is the output of the doSomething rpc";
}
}
}
}
The code that is generated is
using System;
using System.Xml;
using YangSupport;
namespace yangDemo;
///<summary>
///Configuration root object for yangDemo based on provided .yang modules
///</summary>
public class Configuration
{
public Some.Module.YangNode? SomeModule { get; set; }
public async Task WriteXMLAsync(XmlWriter writer)
{
await writer.WriteStartElementAsync(null,"root",null);
if(SomeModule is not null) await SomeModule.WriteXMLAsync(writer);
await writer.WriteEndElementAsync();
}
public static async Task<configuration> ParseAsync(XmlReader reader)
{
Some.Module.YangNode? _SomeModule = default!;
while(await reader.ReadAsync())
{
switch(reader.NodeType)
{
case XmlNodeType.Element:
switch(reader.Name)
{
case "some-module":
_SomeModule = await Some.Module.YangNode.ParseAsync(reader);
continue;
case "rpc-error": throw await RpcException.ParseAsync(reader);
default: throw new Exception($"Unexpected element '{reader.Name}' under 'root'");
}
case XmlNodeType.EndElement when reader.Name == "root":
return new Configuration{
SomeModule = _SomeModule,
};
case XmlNodeType.Whitespace: break;
default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'root'");
}
}
throw new Exception("Reached end-of-readability without ever returning from Configuration.ParseAsync");
}
}
public static class IYangServerExtensions
{
public static async Task Receive(this IYangServer server, global::System.IO.Stream input, global::System.IO.Stream output)
{
var initialPosition = output.Position;
var initialLength = output.Length;
string? id = null;
using XmlReader reader = XmlReader.Create(input, SerializationHelper.GetStandardReaderSettings());
using XmlWriter writer = XmlWriter.Create(output, SerializationHelper.GetStandardWriterSettings());
try
{
await reader.ReadAsync();
switch(reader.Name)
{
case "rpc":
id = reader.ParseMessageId();
await writer.WriteStartElementAsync(null, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0");
await writer.WriteAttributeStringAsync(null, "message-id", null, id);
await reader.ReadAsync();
switch(reader.Name)
{
case "action":
await server.ReceiveAction(reader, writer);
break;
default:
await server.ReceiveRPC(reader, writer);
break;
}
await writer.WriteEndElementAsync();
await writer.FlushAsync();
break;
case "notification":
var eventTime = await reader.ParseEventTime();
await reader.ReadAsync();
await server.ReceiveNotification(reader, eventTime);
break;
}
}
catch(RpcException ex)
{
await writer.FlushAsync();
output.Position = initialPosition;
output.SetLength(initialLength);
await ex.SerializeAsync(output,id);
}
catch(Exception ex)
{
await writer.FlushAsync();
output.Position = initialPosition;
output.SetLength(initialLength);
await output.SerializeRegularExceptionAsync(ex,id);
}
}
public static async Task ReceiveRPC(this IYangServer server, XmlReader reader, XmlWriter writer)
{
switch(reader.Name)
{
case "doSomething" when reader.NamespaceURI is "urn:dotnet:yang:andrei":
{
var input = await Some.Module.YangNode.DoSomethingInput.ParseAsync(reader);
var task = server.OnDoSomething(input);
var response = await task;
await response.WriteXMLAsync(writer);
}
break;
}
}
public static async Task ReceiveAction(this IYangServer server, XmlReader reader, XmlWriter writer)
{
await reader.ReadAsync();
switch(reader.Name)
{
}
}
public static async Task ReceiveNotification(this IYangServer server, XmlReader reader, DateTime eventTime)
{
switch(reader.Name)
{
}
}
}
using System;
using System.Xml;
using System.Text;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using YangSupport;
namespace yangDemo
{
public partial interface IYangServer
{
Task<some.module.yangnode.dosomethingoutput> OnDoSomething(Some.Module.YangNode.DoSomethingInput input);
}
}
namespace Some.Module{
public class YangNode
{
public const string ModuleName = "some-module";
public const string Revision = "";
public static string[] Features = [];
//Yang Version 1.1
public const string Namespace = "urn:dotnet:yang:andrei";
public static string GetEncodedValue(SomeIdentityIdentity value)
{
switch(value)
{
case SomeIdentityIdentity.SomeIdentity: return "someIdentity";
case SomeIdentityIdentity.SomeOtherIdentity: return "someOtherIdentity";
default: return value.ToString();
}
}
public static string GetEncodedValue(SomeIdentityIdentity? value) => GetEncodedValue(value!.Value!);
public static SomeIdentityIdentity GetSomeIdentityIdentityValue(string value)
{
switch(value)
{
case "someIdentity": return SomeIdentityIdentity.SomeIdentity;
case "someOtherIdentity": return SomeIdentityIdentity.SomeOtherIdentity;
default: throw new Exception($"{value} is not a valid value for SomeIdentityIdentity");
}
}
public enum SomeIdentityIdentity
{
SomeIdentity,
SomeOtherIdentity
}
public static string GetEncodedValue(SomeOtherIdentityIdentity value)
{
switch(value)
{
case SomeOtherIdentityIdentity.SomeOtherIdentity: return "someOtherIdentity";
default: return value.ToString();
}
}
public static string GetEncodedValue(SomeOtherIdentityIdentity? value) => GetEncodedValue(value!.Value!);
public static SomeOtherIdentityIdentity GetSomeOtherIdentityIdentityValue(string value)
{
switch(value)
{
case "someOtherIdentity": return SomeOtherIdentityIdentity.SomeOtherIdentity;
default: throw new Exception($"{value} is not a valid value for SomeOtherIdentityIdentity");
}
}
public enum SomeOtherIdentityIdentity
{
SomeOtherIdentity
}
public static async Task<some.module.yangnode.dosomethingoutput> DoSomething(IChannel channel, int messageID, Some.Module.YangNode.DoSomethingInput input)
{
using XmlWriter writer = XmlWriter.Create(channel.WriteStream, SerializationHelper.GetStandardWriterSettings());
await writer.WriteStartElementAsync(null,"rpc","urn:ietf:params:xml:ns:netconf:base:1.0");
await writer.WriteAttributeStringAsync(null,"message-id",null,messageID.ToString());
await writer.WriteStartElementAsync("","doSomething","urn:dotnet:yang:andrei");
await input.WriteXMLAsync(writer);
await writer.WriteEndElementAsync();
await writer.WriteEndElementAsync();
await writer.FlushAsync();
await channel.Send();
using XmlReader reader = XmlReader.Create(channel.ReadStream, SerializationHelper.GetStandardReaderSettings());
await reader.ReadAsync();
if(reader.NodeType != XmlNodeType.Element || reader.Name != "rpc-reply" || reader.NamespaceURI != "urn:ietf:params:xml:ns:netconf:base:1.0" || reader["message-id"] != messageID.ToString())
{
throw new Exception($"Expected stream to start with a <rpc-reply> element with message id {messageID} & \"urn:ietf:params:xml:ns:netconf:base:1.0\" but got {reader.NodeType}: {reader.Name} in {reader.NamespaceURI}");
}
var value = await DoSomethingOutput.ParseAsync(reader);
return value;
}
public class DoSomethingOutput
{
///<summary>
///The identity that is the output of the doSomething rpc
///</summary>
public SomeIdentityIdentity? Response { get; set; } = SomeIdentityIdentity.SomeOtherIdentity;
public static async Task<dosomethingoutput> ParseAsync(XmlReader reader)
{
SomeIdentityIdentity? _Response = default!;
while(await reader.ReadAsync())
{
switch(reader.NodeType)
{
case XmlNodeType.Element:
switch(reader.Name)
{
case "response":
await reader.ReadAsync();
if(reader.NodeType != XmlNodeType.Text)
{
throw new Exception($"Expected token in ParseCall for 'response' to be text, but was '{reader.NodeType}'");
}
_Response = GetSomeIdentityIdentityValue(await reader.GetValueAsync());
if(!reader.IsEmptyElement)
{
await reader.ReadAsync();
if(reader.NodeType != XmlNodeType.EndElement)
{
throw new Exception($"Expected token in ParseCall for 'response' to be an element closure, but was '{reader.NodeType}'");
}
}
continue;
case "rpc-error": throw await RpcException.ParseAsync(reader);
default: throw new Exception($"Unexpected element '{reader.Name}' under 'rpc-reply'");
}
case XmlNodeType.EndElement when reader.Name == "rpc-reply":
return new DoSomethingOutput{
Response = _Response,
};
case XmlNodeType.Whitespace: break;
default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'rpc-reply'");
}
}
throw new Exception("Reached end-of-readability without ever returning from DoSomethingOutput.ParseAsync");
}
public async Task WriteXMLAsync(XmlWriter writer)
{
if(Response != default)
{
await writer.WriteStartElementAsync(null,"response","urn:dotnet:yang:andrei");
await writer.WriteStringAsync(YangNode.GetEncodedValue(Response!));
await writer.WriteEndElementAsync();
}
}
}
public class DoSomethingInput
{
///<summary>
///The value that is the input of the doSomething rpc
///</summary>
public uint? TheBigLeaf { get; set; } = 4;
public async Task WriteXMLAsync(XmlWriter writer)
{
if(TheBigLeaf != default)
{
await writer.WriteStartElementAsync(null,"the-big-leaf","urn:dotnet:yang:andrei");
await writer.WriteStringAsync(TheBigLeaf!.ToString());
await writer.WriteEndElementAsync();
}
}
public static async Task<dosomethinginput> ParseAsync(XmlReader reader)
{
uint? _TheBigLeaf = default!;
while(await reader.ReadAsync())
{
switch(reader.NodeType)
{
case XmlNodeType.Element:
switch(reader.Name)
{
case "the-big-leaf":
await reader.ReadAsync();
if(reader.NodeType != XmlNodeType.Text)
{
throw new Exception($"Expected token in ParseCall for 'the-big-leaf' to be text, but was '{reader.NodeType}'");
}
_TheBigLeaf = uint.Parse(await reader.GetValueAsync());
if(!reader.IsEmptyElement)
{
await reader.ReadAsync();
if(reader.NodeType != XmlNodeType.EndElement)
{
throw new Exception($"Expected token in ParseCall for 'the-big-leaf' to be an element closure, but was '{reader.NodeType}'");
}
}
continue;
case "rpc-error": throw await RpcException.ParseAsync(reader);
default: throw new Exception($"Unexpected element '{reader.Name}' under 'doSomething'");
}
case XmlNodeType.EndElement when reader.Name == "doSomething":
return new DoSomethingInput{
TheBigLeaf = _TheBigLeaf,
};
case XmlNodeType.Whitespace: break;
default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'doSomething'");
}
}
throw new Exception("Reached end-of-readability without ever returning from DoSomethingInput.ParseAsync");
}
}
public static async Task<some.module.yangnode> ParseAsync(XmlReader reader)
{
while(await reader.ReadAsync())
{
switch(reader.NodeType)
{
case XmlNodeType.Element:
switch(reader.Name)
{
case "rpc-error": throw await RpcException.ParseAsync(reader);
default: throw new Exception($"Unexpected element '{reader.Name}' under 'some-module'");
}
case XmlNodeType.EndElement when reader.Name == "some-module":
return new Some.Module.YangNode{
};
case XmlNodeType.Whitespace: break;
default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'some-module'");
}
}
throw new Exception("Reached end-of-readability without ever returning from Some.Module.YangNode.ParseAsync");
}
public async Task WriteXMLAsync(XmlWriter writer)
{
await writer.WriteStartElementAsync(null,"some-module","urn:dotnet:yang:andrei");
await writer.WriteEndElementAsync();
}
}
}
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/DotnetYang