I am new to the C# community. I created a controller and a class with the class containing some logic from a COM reference that I want to be executed.
When I call the API, it does not seem to be going into the ImportInventory method and no logic is executed in that public string (as seen from the debugging breakpoints).
Can someone please assist on how to get this part of the code executed? I am new to C# and can't seem to find the issue.
Code snippet of the controller:
using MDRDS_PastelIntegrator.Models;
using Microsoft.AspNetCore.Mvc;
namespace MDRDS_PastelIntegrator.Controllers
{
[ApiController]
[Route("[controller]")]
public class InventoryController : ControllerBase
{
private readonly ILogger<InventoryController> _logger;
public InventoryController(ILogger<InventoryController> logger)
{
_logger = logger;
}
[HttpPost(Name = "POSTInventory")]
public IEnumerable<POSTInventory> Get(string pParameter, string pPath)
{
return Enumerable.Range(1, 1).Select(index => new POSTInventory
{
Parameter = pParameter,
Path = pPath
})
.ToArray();
}
}
}
Code snippet of the class:
namespace MDRDS_PastelIntegrator.Models
{
public class POSTInventory
{
//public string? StrReturn;
public string? Parameter { get; set; }
public string? Path { get; set; }
public string ImportInventory(string Parameter, string Path)
{
var SDK = new PasSDK.PastelPartnerSDK();
//Set License
var F_GetLisence = new SetLicense();
F_GetLisence.MethodSetLicense();
//Set Data Path
var StrReturn = SDK.SetDataPath(Path);
if (StrReturn == "0")
{
var StrIn = Parameter;
var StrCodeIn = StrIn;
//Import Inventory Item
StrReturn = SDK.ImportInventory(StrIn);
};
//Blank return string - No serial number
if (StrReturn.Length == 0)
{
StrReturn = "Serial Number Not Specified.";
//return StrReturn;
};
//Get Result Code
if (StrReturn == "0")
{
StrReturn = "0 = Success";
}
else
{
StrReturn = "1 = Unsuccessfull";
};
return StrReturn;
}
}
}
You create an object POSTInventory but you never call the ImportInventory method of this object
Related
I have a Json file, it contains connectionstring. I want to asynchronously read the file and deserialize it to a ConnectionString object and I always get a null result. I'm using .NET Core 6 and System.Text.Json.
Here is contents of my Json file:
{
"ConnectionStrings": {
"ConnStr": "Data Source=(local);Initial Catalog=MyData;Integrated Security=False;TrustServerCertificate=True;Persist Security Info=False;Async=True;MultipleActiveResultSets=true;User ID=sa;Password=MySecret;",
"ProviderName": "SQLServer"
}
}
Here are the contents of my classes:
internal class DBConnectionString
{
[JsonPropertyName("ConnStr")]
public string ConnStr { get; set; }
[JsonPropertyName("ProviderName")]
public string ProviderName { get; set; }
public DBConnectionString()
{
}
}
public class DBConnStr {
private static string AppSettingFilePath => "appsettings.json";
public static async Task<string> GetConnectionStringAsync()
{
string connStr = "";
if (File.Exists((DBConnStr.AppSettingFilePath)))
{
using (FileStream sr = new FileStream(AppSettingFilePath, FileMode.Open, FileAccess.Read))
{
//string json = await sr.ReadToEndAsync();
System.Text.Json.JsonDocumentOptions docOpt = new System.Text.Json.JsonDocumentOptions() { AllowTrailingCommas = true };
using (var document = await System.Text.Json.JsonDocument.ParseAsync(sr, docOpt))
{
System.Text.Json.JsonSerializerOptions opt = new System.Text.Json.JsonSerializerOptions() { AllowTrailingCommas = true, PropertyNameCaseInsensitive = true };
System.Text.Json.JsonElement root = document.RootElement;
System.Text.Json.JsonElement element = root.GetProperty("ConnectionStrings");
sr.Position = 0;
var dbConStr = await System.Text.Json.JsonSerializer.DeserializeAsync<DBConnectionString>(sr, opt);
if (dbConStr != null)
{
connStr = dbConStr.ConnStr;
}
}
}
}
return connStr;
}
}
The following is the syntax that I use to call the GetConnectionStringAsync method:
string ConnectionString = DBConnStr.GetConnectionStringAsync().Result;
When the application is running in debug mode, I checked, on line
var dbConStr = await
System.Text.Json.JsonSerializer.DeserializeAsync(sr,
opt);
The DBConnectionString object property is always empty.
I also tried the reference on the Microsoft website, https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/how-to?pivots=dotnet-6-0 but it doesn't work succeed.
using System.Text.Json;
namespace DeserializeFromFileAsync
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
public class Program
{
public static async Task Main()
{
string fileName = "WeatherForecast.json";
using FileStream openStream = File.OpenRead(fileName);
WeatherForecast? weatherForecast =
await JsonSerializer.DeserializeAsync<WeatherForecast>(openStream);
Console.WriteLine($"Date: {weatherForecast?.Date}");
Console.WriteLine($"TemperatureCelsius: {weatherForecast?.TemperatureCelsius}");
Console.WriteLine($"Summary: {weatherForecast?.Summary}");
}
}
}
Do you have a solution for my problem or a better solution? I appreciate all your help. Thanks
Sorry about my English if it's not good, because I'm not fluent in English and use google translate to translate it
To begin with, if you want to read information from appSettings.json, you should explore more into reading configurations. There are helper classes provided by .Net for the same.
Coming back to your code, if you want to use your own code for Json Deserialization, then you need to make the following change to it.
var dbConStr = System.Text.Json.JsonSerializer.Deserialize<DBConnectionString>(element.GetRawText(), opt);
where, element according to code shared in the question is defined as
System.Text.Json.JsonElement element = root.GetProperty("ConnectionStrings");
This ensures the Raw Json associated with the JsonElement ConnectStrings is de-serialized.
However, I recommend you to read more into Reading configurations using the IConfiguration and related .Net helpers.
This question already has answers here:
My console app shutdown prematurely when using async / await?
(4 answers)
Program exits upon calling await
(3 answers)
Closed 2 years ago.
Before you all go on a rampage about how this is a duplicate question, I have spent two days working on this issue, watching youtube tutorials on asynchronous programming, surfing similar stackoverflow posts etc, and I cannot for the life of me figure out how to apply Asynchronous Parallel Downloading of files into my project.
First things first, some background:
I am creating a program that, when given a query input via the user, will make a call to the twitch API and download clips.
My program is two parts
1- A web scraper that generates a .json file with all details needed to download files and
2 - A downloader.
Part 1 works perfectly fine and generates the .json files no trouble.
My Downloader contains reference to a Data class that is a handler for common properties and methods like my ClientID, Authentication, OutputPath, JsonFile, QueryURL. It also contains methods to give values to these properties.
Here are the two methods of my FileDownloader.cs that are the problem:
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach(ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
This is only one of my many attempts of downloading files, one which I got the idea from this post:
stackoverflow_link
I have also tried methods like the following from a YouTube video by IAmTimCorey:
video_link
I have spent many an hour tackling this problem, and I honestly can't figure out why it won't work with any of my attempts. I would vastly appreciate your help.
Thanks,
Ben
Below is the entirety of my code, should anyone need it for any reason.
Code Structure:
The only external libraries I have downloaded is Newtonsoft.Json
ClipInfo.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Downloader
{
public class ClipInfo
{
public string id { get; set; }
public string url { get; set; }
public string embed_url { get; set; }
public string broadcaster_id { get; set; }
public string broadcaster_name { get; set; }
public string creator_id { get; set; }
public string creator_name { get; set; }
public string video_id { get; set; }
public string game_id { get; set; }
public string language { get; set; }
public string title { get; set; }
public int view_count { get; set; }
public DateTime created_at { get; set; }
public string thumbnail_url { get; set; }
}
}
Pagination.cs
namespace Downloader
{
public class Pagination
{
public string cursor { get; set; }
}
}
Root.cs
using System.Collections.Generic;
namespace Downloader
{
public class Root
{
public List<ClipInfo> data { get; set; }
public Pagination pagination { get; set; }
}
}
Data.cs
using System;
using System.IO;
namespace Downloader
{
public class Data
{
private static string directory = Directory.GetCurrentDirectory();
private readonly static string defaultJsonFile = directory + #"\clips.json";
private readonly static string defaultOutputPath = directory + #"\Clips\";
private readonly static string clipsLink = "https://api.twitch.tv/helix/clips?";
public string OutputPath { get; set; }
public string JsonFile { get; set; }
public string ClientID { get; private set; }
public string Authentication { get; private set; }
public string QueryURL { get; private set; }
public Data()
{
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication, string outputPath)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = directory + #"\" + outputPath + #"\";
JsonFile = OutputPath + outputPath + ".json";
}
public void GetQuery()
{
Console.Write("Please enter your query: ");
QueryURL = clipsLink + Console.ReadLine();
}
public void GetClientID()
{
Console.WriteLine("Enter your client ID");
ClientID = Console.ReadLine();
}
public void GetAuthentication()
{
Console.WriteLine("Enter your Authentication");
Authentication = Console.ReadLine();
}
public void OutputFolderExists()
{
if (!Directory.Exists(OutputPath))
{
Directory.CreateDirectory(OutputPath);
}
}
}
}
JsonGenerator.cs
using System;
using System.IO;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Linq;
namespace Downloader
{
public static class JsonGenerator
{
// This class has no constructor.
// You call the Generate methods, passing in all required data.
// The file will then be generated.
private static Data data;
public static async Task Generate(Data clientData)
{
data = clientData;
string responseContent = null;
// Loop that runs until the api request goes through
bool authError = true;
while (authError)
{
authError = false;
try
{
responseContent = await GetHttpResponse();
}
catch (HttpRequestException)
{
Console.WriteLine("Invalid authentication, please enter client-ID and authentication again!");
data.GetClientID();
data.GetAuthentication();
authError = true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
authError = true;
}
}
data.OutputFolderExists();
GenerateJson(responseContent);
}
// Returns the contents of the resopnse to the api call as a string
private static async Task<string> GetHttpResponse()
{
// Creating client
HttpClient client = new HttpClient();
if (data.QueryURL == null)
{
data.GetQuery();
}
// Setting up request
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, data.QueryURL);
// Adding Headers to request
requestMessage.Headers.Add("client-id", data.ClientID);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", data.Authentication);
// Receiving response to the request
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage);
// Gets the content of the response as a string
string responseContent = await responseMessage.Content.ReadAsStringAsync();
return responseContent;
}
// Generates or adds to the .json file that contains data on each clip
private static void GenerateJson(string responseContent)
{
// Parses the data from the response to the api request
Root responseResult = JsonConvert.DeserializeObject<Root>(responseContent);
// If the file doesn't exist, we need to create it and add a '[' at the start
if (!File.Exists(data.JsonFile))
{
FileStream file = File.Create(data.JsonFile);
file.Close();
// The array of json objects needs to be wrapped inside []
File.AppendAllText(data.JsonFile, "[\n");
}
else
{
// For a pre-existing .json file, The last object won't have a comma at the
// end of it so we need to add it now, before we add more objects
string[] jsonLines = File.ReadAllLines(data.JsonFile);
File.WriteAllLines(data.JsonFile, jsonLines.Take(jsonLines.Length - 1).ToArray());
File.AppendAllText(data.JsonFile, ",");
}
// If the file already exists, but there was no [ at the start for whatever reason,
// we need to add it
if (File.ReadAllText(data.JsonFile).Length == 0 || File.ReadAllText(data.JsonFile)[0] != '[')
{
File.WriteAllText(data.JsonFile, "[\n" + File.ReadAllText(data.JsonFile));
}
string json;
// Loops through each ClipInfo object that the api returned
for (int i = 0; i < responseResult.data.Count; i++)
{
// Serializes the ClipInfo object into a json style string
json = JsonConvert.SerializeObject(responseResult.data[i]);
// Adds the serialized contents of ClipInfo to the .json file
File.AppendAllText(data.JsonFile, json);
if (i != responseResult.data.Count - 1)
{
// All objects except the last require a comma at the end of the
// object in order to correctly format the array of json objects
File.AppendAllText(data.JsonFile, ",");
}
// Adds new line after object entry
File.AppendAllText(data.JsonFile, "\n");
}
// Adds the ] at the end of the file to close off the json objects array
File.AppendAllText(data.JsonFile, "]");
}
}
}
FileDownloader.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace Downloader
{
public class FileDownloader
{
private static Data data;
private static List<Task> tasks;
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach (ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private static void GetData()
{
if (data.ClientID == null)
{
data.GetClientID();
}
if (data.Authentication == null)
{
data.GetAuthentication();
}
if (data.QueryURL == null)
{
data.GetQuery();
}
}
private static string GetClipURL(ClipInfo clip)
{
// Example thumbnail URL:
// https://clips-media-assets2.twitch.tv/AT-cm%7C902106752-preview-480x272.jpg
// You can get the URL of the location of clip.mp4
// by removing the -preview.... from the thumbnail url */
string url = clip.thumbnail_url;
url = url.Substring(0, url.IndexOf("-preview")) + ".mp4";
return url;
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
private static void FileDownloadComplete(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
tasks.Remove((Task)sender);
}
}
}
Program.cs
using System;
using System.Threading.Tasks;
using Downloader;
namespace ClipDownloader
{
class Program
{
private static string clientID = "{your_client_id}";
private static string authentication = "{your_authentication}";
async static Task Main(string[] args)
{
Console.WriteLine("Enter your output path");
string outputPath = Console.ReadLine();
Data data = new Data(clientID, authentication, outputPath);
Console.WriteLine(data.OutputPath);
//await JsonGenerator.Generate(data);
FileDownloader.DownloadAllFiles(data);
}
}
}
The example query I usually type in is "game_id=510218"
async void is your problem
Change
public static async void DownloadAllFiles(Data clientData)
To
public static async Task DownloadAllFiles(Data clientData)
Then you can await it
await FileDownloader.DownloadAllFiles(data);
The longer story:
async void runs unobserved (fire and forget). You can't wait for them to finish. In essence as soon as your program starts the task, it finishes, and tears down the App Domain and all your sub tasks, leading you to believe nothing is working.
I'm trying to stay on topic here as best as I can, but when using JsonConvert.DeserializeObject{T}, isn't T suppose to be an encapsulating root object type? I have never used it the way you're using it, so I'm just curious if that might be your bug. I could be completely wrong, and spare me if i am, but JSON is key:value based. Deserializing directly to a List doesn't really make sense. Unless there is a special case in the deserializer? List would be a file that's purely an array of ClipInfo values being deserialized into the members of List{T}(private T[] _items, private int _size, etc.) It needs a parent root object.
// current JSON file format implication(which i dont think is valid JSON?(correct me please)
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
// correct(?) JSON file format
{ // { } is the outer encasing object
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
}
class ClipInfoJSONFile
{
public List<ClipInfo> Info { get; set; }
}
var clipInfoList = JsonConverter.DeserializeObject<ClipInfoJSONFile>(...);
I managed to get it working using the following code:
.AddNewtonsoftJson(options => {
options.SerializerSettings.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
};
});
However this makes MVC use Newtonsoft rather than System.Text.JSON which is faster, async and built in.
Looking at the naming policy options in System.Text.JSON I could only find CamelCase. Is there any native support for snake case? What is a better way of achieving snake case JSON naming style?
Just slight modification in pfx code to remove the dependency on Newtonsoft Json.Net.
String extension method to convert the given string to SnakeCase.
public static class StringUtils
{
public static string ToSnakeCase(this string str)
{
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
}
}
Then in our SnakeCaseNamingPolicy we can do
public class SnakeCaseNamingPolicy : JsonNamingPolicy
{
public static SnakeCaseNamingPolicy Instance { get; } = new SnakeCaseNamingPolicy();
public override string ConvertName(string name)
{
// Conversion to other naming convention goes here. Like SnakeCase, KebabCase etc.
return name.ToSnakeCase();
}
}
The last step is to register our naming policy in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(
options => {
options.JsonSerializerOptions.PropertyNamingPolicy =
SnakeCaseNamingPolicy.Instance;
});
}
Using the model:
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelcius { get; set; }
public int TemperatureFahrenheit { get; set; }
public string Summary { get; set; }
}
Json output:
{
"date": "2019-10-28T08:26:14.878444+05:00",
"temperature_celcius": 4,
"temperature_fahrenheit": 0,
"summary": "Scorching"
}
At the moment there's no builtin support for snake case,
but .NET Core 3.0 allows to set up a custom naming policy by inheriting from JsonNamingPolicy.
You need to implement the ConvertName method with the snake case conversion.
(Newtonsoft Json.NET has an internal StringUtils class which shows how to handle this.)
The POC implementation below, re-uses Json.NET's SnakeCaseNamingStrategy only for the snake case conversion (, whereas the whole application uses System.Text.Json).
It is better to avoid having a dependency on Newtonsoft Json.Net for only the snake case conversion, but in this rather LAZY example below I don't want to rethink/reinvent a snake case conversion method.
The main point of this answer is how to hook a custom policy (and not the snake case conversion itself.) (There are many libraries and code samples that show how to do so.)
public class SnakeCaseNamingPolicy : JsonNamingPolicy
{
private readonly SnakeCaseNamingStrategy _newtonsoftSnakeCaseNamingStrategy
= new SnakeCaseNamingStrategy();
public static SnakeCaseNamingPolicy Instance { get; } = new SnakeCaseNamingPolicy();
public override string ConvertName(string name)
{
/* A conversion to snake case implementation goes here. */
return _newtonsoftSnakeCaseNamingStrategy.GetPropertyName(name, false);
}
}
In Startup.cs you apply this custom SnakeCaseNamingPolicy.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(
options => {
options.JsonSerializerOptions.PropertyNamingPolicy =
SnakeCaseNamingPolicy.Instance;
});
}
An instance of the class below
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelcius { get; set; }
public int TemperatureFahrenheit { get; set; }
[JsonPropertyName("Description")]
public string Summary { get; set; }
}
will have a Json representation as:
{ "date" : "2019-10-28T01:00:56.6498885+01:00",
"temperature_celcius" : 48,
"temperature_fahrenheit" : 118,
"Description" : "Cool"
}
Note that the property Summary has been given the name Description,
which matches its System.Text.Json.Serialization.JsonPropertyNameAttribute.
I share a full implementation of #pfx 's solution here. The custom naming policy (copied from NewtonSoft):
using System.Text;
using System.Text.Json;
namespace Utils
{
public class SnakeCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name) => JsonUtils.ToSnakeCase(name);
}
public class JsonUtils
{
private enum SeparatedCaseState
{
Start,
Lower,
Upper,
NewWord
}
public static string ToSnakeCase(string s) => ToSeparatedCase(s, '_');
private static string ToSeparatedCase(string s, char separator)
{
if (string.IsNullOrEmpty(s))
{
return s;
}
StringBuilder sb = new StringBuilder();
SeparatedCaseState state = SeparatedCaseState.Start;
for (int i = 0; i < s.Length; i++)
{
if (s[i] == ' ')
{
if (state != SeparatedCaseState.Start)
{
state = SeparatedCaseState.NewWord;
}
}
else if (char.IsUpper(s[i]))
{
switch (state)
{
case SeparatedCaseState.Upper:
bool hasNext = (i + 1 < s.Length);
if (i > 0 && hasNext)
{
char nextChar = s[i + 1];
if (!char.IsUpper(nextChar) && nextChar != separator)
{
sb.Append(separator);
}
}
break;
case SeparatedCaseState.Lower:
case SeparatedCaseState.NewWord:
sb.Append(separator);
break;
}
char c;
c = char.ToLowerInvariant(s[i]);
sb.Append(c);
state = SeparatedCaseState.Upper;
}
else if (s[i] == separator)
{
sb.Append(separator);
state = SeparatedCaseState.Start;
}
else
{
if (state == SeparatedCaseState.NewWord)
{
sb.Append(separator);
}
sb.Append(s[i]);
state = SeparatedCaseState.Lower;
}
}
return sb.ToString();
}
}
}
The following model is used in the simple example:
public class TestSerializer
{
public DateTime TimeStamp { get; set; }
public int CPUPower { get; set; }
}
And the example usage:
var data = new TestSerializer();
data.TimeStamp = DateTime.Now;
data.CPUPower = 10;
var serializeOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = new SnakeCaseNamingPolicy()
};
var json_string = JsonSerializer.Serialize(data, serializeOptions);
Console.WriteLine(json_string);
gives {"time_stamp":"2020-08-06T00:30:35.3815583-04:00","cpu_power":10}
There is a GitHub Repository with SnakeCase and KebabCase support for System.Text.Json, also a nuget package is available.
nuget
PM> Install-Package JorgeSerrano.Json.JsonSnakeCaseNamingPolicy
SnakeCase NamingPolicy
var person = new Person { FirstName = "Jorge", Birthday = DateTime.UtcNow, MyJobCity = "Madrid" };
var options = new JsonSerializerOptions { PropertyNamingPolicy = new JsonSnakeCaseNamingPolicy() };
var json = JsonSerializer.Serialize(person, options);
KebabCase NamingPolicy
var person = new Person { FirstName = "Jorge", Birthday = DateTime.UtcNow, MyJobCity = "Madrid" };
var options = new JsonSerializerOptions { PropertyNamingPolicy = new JsonKebabCaseNamingPolicy() };
var json = JsonSerializer.Serialize(person, options);
It is a bit late, but this solution will also solve cases like ABCItem or MyCPU.
This is just the concept, you can refine it to make it more versatile
using System.Collections.Generic;
namespace Extensions
{
public static class StringExtension
{
public static string ToSnakeCase(this string str)
{
// collect the final result
var snakeCase = new List<char>();
// check and add chars (using for loop for performance)
for (int i = 0; i < str.Length; i++)
{
if (i > 0 && char.IsUpper(str[i]) && !char.IsUpper(str[i + 1]))
{
snakeCase.Add('_');
}
snakeCase.Add(str[i]);
}
// build the new string
return new string(snakeCase.ToArray()).ToLower();
}
}
}
var snakeCase = "CPUMeter".ToSnakeCase();
var snakeCase = "PascalCase".ToSnakeCase();
var snakeCase = "camelCase".ToSnakeCase();
There's no need for a seperate kebab- and snake- case naming policy.
Also, if you absolutely want to minimize the number of unnecceesary character-conversions and lookups, the conversion method can be somewhat optimized
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
public class SeperatorNamingPolicy : JsonNamingPolicy
{
public SeperatorNamingPolicy(char seperator = '_')
{
Seperator = seperator;
}
public char Seperator { get; }
public override string ConvertName(string name)
{
IEnumerable<char> ToSeperated()
{
var e = name.GetEnumerator();
if (!e.MoveNext()) yield break;
yield return char.ToLower(e.Current);
while (e.MoveNext())
{
if (char.IsUpper(e.Current))
{
yield return Seperator;
yield return char.ToLower(e.Current);
}
else
{
yield return e.Current;
}
}
}
return new string(ToSeperated().ToArray());
}
}
However, if you just want a snake case naming policy without adding additional dependencies to your code, a dedicated snakecasenamingpolicy suffices:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
public class SnakeCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
static IEnumerable<char> ToSnakeCase(CharEnumerator e)
{
if (!e.MoveNext()) yield break;
yield return char.ToLower(e.Current);
while (e.MoveNext())
{
if (char.IsUpper(e.Current))
{
yield return '_';
yield return char.ToLower(e.Current);
}
else
{
yield return e.Current;
}
}
}
return new string(ToSnakeCase(name.GetEnumerator()).ToArray());
}
}
You can of course use this by adding the json options in your startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// . . .
services
.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions.PropertyNamingPolicy = new SnakeCaseNamingPolicy());
// . . .
}
I have had a static version of this type of code working in a static version. However the API calls were just incredibly slow. I am trying to move to asynchronous now that C# 7 supports console async tasks (where I add code to connect to my DB and store data. I want to see this code output on the console to ensure it's working so I can assign variables for loading. I can't seem to figure out how to access the list from main. Here is the code I have so far:
Wrapper (or C# library):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace AlphaVantageApiWrapper
{
public static class AlphaVantageApiWrapper
{
public static async Task<AlphaVantageRootObject> GetTechnical(List<ApiParam> parameters, string apiKey)
{
var stringRequest = parameters.Aggregate(#"https://www.alphavantage.co/query?", (current, param) => current + param.ToApiString());
stringRequest += "&apikey=" + apiKey;
var apiData = await CallAlphaVantageApi(stringRequest);
var technicalsObject = new AlphaVantageRootObject
{
MetaData = new MetaData
{
Function = parameters.FirstOrDefault(x => x.ParamName.Equals("function"))?.ParamValue ?? "NA?",
Interval = parameters.FirstOrDefault(x => x.ParamName.Equals("interval"))?.ParamValue ?? "NA?",
SeriesType = parameters.FirstOrDefault(x => x.ParamName.Equals("series_type"))?.ParamValue ?? "NA?",
Symbol = parameters.FirstOrDefault(x => x.ParamName.Equals("symbol"))?.ParamValue ?? "NA?"
},
TechnicalsByDate = apiData.Last.Values().OfType<JProperty>().Select(x => new TechnicalDataDate
{
Date = Convert.ToDateTime(x.Name),
Data = x.Value.OfType<JProperty>().Select(r => new TechnicalDataObject
{
TechnicalKey = r.Name,
TechnicalValue = Convert.ToDouble(r.Value.ToString())
}).ToList()
})
.ToList()
};
return technicalsObject;
}
public class ApiParam
{
public string ParamName;
public string ParamValue;
public ApiParam(string paramNameIn, string paramValueIn)
{
ParamName = paramNameIn;
ParamValue = paramValueIn;
}
public string ToApiString()
{
return $"&{ParamName}={ParamValue}";
}
}
public static string ToDescription(this Enum enumeration)
{
var type = enumeration.GetType();
var memInfo = type.GetMember(enumeration.ToString());
if (memInfo.Length <= 0) return enumeration.ToString();
var attrs = memInfo[0].GetCustomAttributes(typeof(EnumDescription), false);
return attrs.Length > 0 ? ((EnumDescription)attrs[0]).Text : enumeration.ToString();
}
public static async Task<JObject> CallAlphaVantageApi(string stringRequest)
{
try
{
using (var client = new HttpClient())
{
var res = await client.GetStringAsync(stringRequest);
return JsonConvert.DeserializeObject<JObject>(res);
}
}
catch (Exception e)
{
//fatal error
return null;
}
}
public class AlphaVantageRootObject
{
public MetaData MetaData;
public List<TechnicalDataDate> TechnicalsByDate;
}
public class MetaData
{
public string Function;
public string Interval;
public string SeriesType;
public string Symbol;
}
public class TechnicalDataDate
{
public DateTime Date;
public List<TechnicalDataObject> Data;
}
public class TechnicalDataObject
{
public string TechnicalKey { get; set; }
public double TechnicalValue { get; set; }
}
public class EnumDescription : Attribute
{
public string Text { get; }
public EnumDescription(string text)
{
Text = text;
}
}
public enum AvFuncationEnum
{
[EnumDescription("SMA")] Sma,
[EnumDescription("EMA")] Ema,
[EnumDescription("MACD")] Macd,
[EnumDescription("STOCH")] Stoch,
[EnumDescription("RSI")] Rsi,
}
public enum AvIntervalEnum
{
[EnumDescription("1min")] OneMinute,
[EnumDescription("5min")] FiveMinutes,
[EnumDescription("15min")] FifteenMinutes,
[EnumDescription("30min")] ThirtyMinutes,
[EnumDescription("60min")] SixtyMinutes,
[EnumDescription("daily")] Daily,
[EnumDescription("weekly")] Weekly,
[EnumDescription("monthly")] Monthly
}
public enum AvSeriesType
{
[EnumDescription("close")] Close,
[EnumDescription("open")] Open,
[EnumDescription("high")] High,
[EnumDescription("low")] Low,
}
}
}
`
The c# async main task (which obviously isn't working)...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AlphaVantageApiWrapper.Test
{
public static class AlphaVantageApiDbLoader
{
public static async Task Main(string[] args)
{
var API_KEY = "EnterAPIHERE";
var StockTickers = new List<string> { "AAPL" }; //eventualy becomes a list pulled in from the DB for processing
foreach (var ticker in StockTickers)
{
var parameters = new List<AlphaVantageApiWrapper.ApiParam>
{
new AlphaVantageApiWrapper.ApiParam("function", AlphaVantageApiWrapper.AvFuncationEnum.Sma.ToDescription()),
new AlphaVantageApiWrapper.ApiParam("symbol", ticker),
new AlphaVantageApiWrapper.ApiParam("interval", AlphaVantageApiWrapper.AvIntervalEnum.Daily.ToDescription()),
new AlphaVantageApiWrapper.ApiParam("time_period", "5"),
new AlphaVantageApiWrapper.ApiParam("series_type", AlphaVantageApiWrapper.AvSeriesType.Open.ToDescription()),
};
//Start Collecting SMA values
var SMA_5 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
///var SMA_5Result = AlphaVantageApiWrapper.TechnicalDataObject() // can't all method error just want values fron list
parameters.FirstOrDefault(x => x.ParamName == "time_period").ParamValue = "20";
var SMA_20 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
parameters.FirstOrDefault(x => x.ParamName == "time_period").ParamValue = "50";
var SMA_50 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
parameters.FirstOrDefault(x => x.ParamName == "time_period").ParamValue = "200";
var SMA_200 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
//Change function to EMA
//Change function to RSI
//Change function to MACD
}
}
}
}
Any help would be greatly appreciated! I know the code runs in the background, I just can't seem to get it to a point to view it on the console screen. Eventually I would assign the symbol, date, value returned variable and read these to a DB. I'm used to using DataTables, but the async and .ToList is new to me. Thanks!!
I'm using Craft.Net.Client library but I've got an error when trying to use the ServerList.SaveTo(string file) method : Unable to read beyond the end of the stream (EndOfStreamException)
ServerList.cs :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using fNbt;
namespace Craft.Net.Client
{
/// <summary>
/// Provides functionality for interacting with
/// the saved vanilla server list.
/// </summary>
public class ServerList
{
public static string ServersDat
{
get
{
return Path.Combine(DotMinecraft.GetDotMinecraftPath(), "servers.dat");
}
}
public ServerList()
{
Servers = new List<Server>();
}
public List<Server> Servers { get; set; }
public void Save()
{
SaveTo(ServersDat);
}
public void SaveTo(string file)
{
var nbt = new NbtFile(file); // ERROR : Unable to read beyond the end of the stream (EndOfStreamException)
nbt.RootTag = new NbtCompound("");
var list = new NbtList("servers", NbtTagType.Compound);
foreach (var server in Servers)
{
var compound = new NbtCompound();
compound.Add(new NbtString("name", server.Name));
compound.Add(new NbtString("ip", server.Ip));
compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
list.Add(compound);
}
nbt.RootTag.Add(list);
nbt.SaveToFile(file, NbtCompression.None);
}
public static ServerList Load()
{
return LoadFrom(ServersDat);
}
public static ServerList LoadFrom(string file)
{
var list = new ServerList();
var nbt = new NbtFile(file);
foreach (NbtCompound server in nbt.RootTag["servers"] as NbtList)
{
var entry = new Server();
if (server.Contains("name"))
entry.Name = server["name"].StringValue;
if (server.Contains("ip"))
entry.Ip = server["ip"].StringValue;
if (server.Contains("hideAddress"))
entry.HideAddress = server["hideAddress"].ByteValue == 1;
if (server.Contains("acceptTextures"))
entry.AcceptTextures = server["acceptTextures"].ByteValue == 1;
list.Servers.Add(entry);
}
return list;
}
public class Server
{
public string Name { get; set; }
public string Ip { get; set; }
public bool HideAddress { get; set; }
public bool AcceptTextures { get; set; }
public override string ToString()
{
return Name;
}
}
}
}
And where I call the method :
ServerList.Server server = new ServerList.Server();
server.Name = "x";
server.Ip = "x";
server.HideAddress = true;
server.AcceptTextures = true;
ServerList list = new ServerList();
list.Servers.Add(server);
if (!File.Exists(RuntimeInfo.getMinecraftDir() + #"\servers.dat"))
{
File.Create(RuntimeInfo.getMinecraftDir() + #"\servers.dat");
}
list.SaveTo(RuntimeInfo.getMinecraftDir() + #"\servers.dat");
One thing I've noticed is that I only got the error when servers.dat is empty but not if it has already servers saved.
Can anyone help me?
Thanks in advance,
EDIT : Thanks to steveg89, the solution below solves the problem (updating SaveTo method) :
public void SaveTo(string file)
{
NbtFile nbt;
if (File.Exists(RuntimeInfo.getMinecraftDir() + #"\servers.dat"))
{
nbt = new NbtFile();
nbt.SaveToFile(RuntimeInfo.getMinecraftDir() + #"\servers.dat", NbtCompression.None);
}
else
nbt = new NbtFile();
nbt.RootTag = new NbtCompound("");
var list = new NbtList("servers", NbtTagType.Compound);
foreach (var server in Servers)
{
var compound = new NbtCompound();
compound.Add(new NbtString("name", server.Name));
compound.Add(new NbtString("ip", server.Ip));
compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
list.Add(compound);
}
nbt.RootTag.Add(list);
nbt.SaveToFile(file, NbtCompression.None);
}
I notice their constructor for an NbtFile tries to read from the file. It's assuming the file is in the correct format already. That means you'd have to create it and save it. Their API SHOULD handle this for you but doesn't, so try this
if (!File.Exists(RuntimeInfo.getMinecraftDir() + #"\servers.dat"))
{
NbtFile nbt = new NbtFile();
nbt.SaveToFile(RuntimeInfo.getMinecraftDir() + #"\servers.dat", Nbt.Compression.None);
}
I think a smarter way to do it would be to correct the SaveTo method of the ServerList. This method would mean you wouldn't have to check it in your code, but it does mean you'd be using your own flavor of Craft.Net. I'd do it like so:
public void SaveTo(string file)
{
NbtFile nbt;
if( File.Exists( file ) )
{
nbt = new NbtFile(file);
}
else
{
nbt = new NbtFile();
}
nbt.RootTag = new NbtCompound("");
var list = new NbtList("servers", NbtTagType.Compound);
foreach (var server in Servers)
{
var compound = new NbtCompound();
compound.Add(new NbtString("name", server.Name));
compound.Add(new NbtString("ip", server.Ip));
compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
list.Add(compound);
}
nbt.RootTag.Add(list);
nbt.SaveToFile(file, NbtCompression.None);
}