Json with C# and Mono - c#

I'm trying to read a json string into memory and get this undocumented error msg
$ mcs -r:FortnoxAPILibrary.dll -r:npgsql.dll -r:System.Data.dll -r:Newtonsoft.Json.dll Vouchers.cs
Vouchers.cs(44,18): error CS0103: The name `JArray' does not exist in the current context
Compilation failed: 1 error(s), 0 warnings
My code is
var json = System.IO.File.ReadAllText("test.json");
var objects = JArray.Parse(json); // parse as array
foreach(JObject root in objects)
{
foreach(KeyValuePair<String, JToken> app in root)
{
var appName = app.Key;
var description = (String)app.Value["Description"];
var value = (String)app.Value["Value"];
Console.WriteLine(appName);
Console.WriteLine(description);
Console.WriteLine(value);
Console.WriteLine("\n");
}
}
Where is it documented how this should work?

You are more than likely missing a using statement.
using Newtonsoft.Json.Linq;
Every piece of C# code you write, except for core types, requires a using statement pointing to any dependencies.
C# libraries often don't document the using statement requirements for a block of code. Maybe an oversight, but most users are using an IDE, which warns of the missing statement and offers options to automatically insert them.

It is not documented that I must include this line.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Related

My System.CommandLine app won't build! It can't find a CommandHandler. Do I need to write it?

I am using VS 2022, .Net 6.0, and trying to build my first app using System.CommandLine.
Problem: when I build it, I get an error
The name 'CommandHandler' does not exist in the current context
The code I'm trying to build is the sample app from the GitHub site: https://github.com/dotnet/command-line-api/blob/main/docs/Your-first-app-with-System-CommandLine.md , without alteration (I think).
It looks like this:
using System;
using System.CommandLine;
using System.IO;
static int Main(string[] args)
{
// Create a root command with some options
var rootCommand = new RootCommand
{
new Option<int>(
"--int-option",
getDefaultValue: () => 42,
description: "An option whose argument is parsed as an int"),
new Option<bool>(
"--bool-option",
"An option whose argument is parsed as a bool"),
new Option<FileInfo>(
"--file-option",
"An option whose argument is parsed as a FileInfo")
};
rootCommand.Description = "My sample app";
// Note that the parameters of the handler method are matched according to the names of the options
rootCommand.Handler = CommandHandler.Create<int, bool, FileInfo>((intOption, boolOption, fileOption) =>
{
Console.WriteLine($"The value for --int-option is: {intOption}");
Console.WriteLine($"The value for --bool-option is: {boolOption}");
Console.WriteLine($"The value for --file-option is: {fileOption?.FullName ?? "null"}");
});
// Parse the incoming args and invoke the handler
return rootCommand.InvokeAsync(args).Result;
}
I have installed the latest version of System.Commandline: 2.0.0-beta2.21617.1
SURELY I am just being a big fat idiot in some respect. But I don't see it.
Any insight would be welcomed.
This issue is caused by updating the CommandLine 2.0 Beta 2 package. Add the reference System.CommandLine.NamingConventionBinder to the references to fix the problem. Follow the announcements on command-line-api's GitHub account:
In your project, add a reference to System.CommandLine.NamingConventionBinder.
In your code, change references to the System.CommandLine.Invocation namespace to
use System.CommandLine.NamingConventionBinder, where the CommandHandler.Create
methods are now found. (There’s no longer a CommandHandler type in
System.CommandLine, so after you update you’ll get compilation errors until you
reference System.CommandLine.NamingConventionBinder.)
If you want to continue with the old habits, try using older versions of the System.CommandLine package.
References
Announcing System.CommandLine 2.0 Beta 2 and the road to GA
Think you are missing a using line:
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
I can't swear that's it, but it looks like CommandHandler is defined in a namespace not referenced by a using (in your current code), so System.CommandLine.Invocation may be the key!

How can I detect unused imports in a Script (rather than a Document) with Roslyn?

I'm writing a system to process snippets written as unit tests for Noda Time, so I can include the snippets in the documentation. I've got a first pass working, but I wanted to tidy up the code. One of the things this needs to do when processing a snippet is work out which of the using directives are actually required for that snippet. (There can be multiple snippets in a single source file, but each snippet will appear separately in the documentation - I don't want imports from one snippet affecting another.)
The working code deals with Document instances - I create a separate Document per snippet containing a single method and all the potential imports, add it to the project, and then remove unnecessary using directives like this:
private async static Task<Document> RemoveUnusedImportsAsync(Document document)
{
var compilation = await document.Project.GetCompilationAsync();
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var unusedImportNodes = compilation.GetDiagnostics()
.Where(d => d.Id == "CS8019")
.Where(d => d.Location?.SourceTree == tree)
.Select(d => root.FindNode(d.Location.SourceSpan))
.ToList();
return document.WithSyntaxRoot(
root.RemoveNodes(unusedImportNodes, SyntaxRemoveOptions.KeepNoTrivia));
}
I've since learned that I could use the IOrganizeImportsService when working with a document, but I'd like to just write it as a Script, as that feels much cleaner in various ways.
Creating the script is easy, so I'd like to just analyze that for unused imports (after some earlier cleanup steps). Here's code I'd hoped would work for a script:
private static Script RemoveUnusedImports(Script script)
{
var compilation = script.GetCompilation();
var tree = compilation.SyntaxTrees.Single();
var root = tree.GetRoot();
var unusedImportNodes = compilation.GetDiagnostics()
.Where(d => d.Id == "CS8019")
.Where(d => d.Location?.SourceTree == tree)
.Select(d => root.FindNode(d.Location.SourceSpan))
.ToList();
var newRoot = root.RemoveNodes(unusedImportNodes, SyntaxRemoveOptions.KeepNoTrivia);
return CSharpScript.Create(newRoot.ToFullString(), script.Options);
}
Unfortunately, that doesn't find any diagnostics at all - they're just not produced in the compilation :(
Here's a short sample app demonstrating that:
using System;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
class Program
{
static void Main(string[] args)
{
string text = #"
using System;
using System.Collections.Generic;
Console.WriteLine(""I only need to use System"");";
Script script = CSharpScript.Create(text);
// Not sure whether this *should* be required, but it doesn't help...
script.Compile();
var compilation = script.GetCompilation();
foreach (var d in compilation.GetDiagnostics())
{
Console.WriteLine($"{d.Id}: {d.GetMessage()}");
}
}
}
Required package: Microsoft.CodeAnalysis.CSharp.Scripting (e.g. v2.1.0)
This produces no output :(
My guess is that this is intended, because scripting usually has different use cases. But is there any way of enabling more diagnostics for scripting purposes? Or is there some alternative way of detecting unused imports in a Script? If not, I'll go back to my Document-based approach - which would be a pity, as everything else seems to work quite nicely with scripts...
As far as I know, the default compilation in the scripting engine doesn't configure diagnostics for anything but syntax errors. Unfortunately the scripting engine only has limited options to configure the underlying compilation yourself.
However, you can probably achieve what you're after by skipping the scripting engine and directly creating the compilation yourself. This is essentially what the script host does behind the scenes with the addition of some of the defaults for the compilation as well as a few fancy things like lifting class declarations. The code to skip the script host and create the compilation yourself would look something like:
using System;
using System.IO;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
class Program
{
static void Main(string[] args)
{
string text = #"
using System;
using System.Collections.Generic;
Console.WriteLine(""I only need to use System"");";
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(text, new CSharpParseOptions(kind: SourceCodeKind.Script));
var coreDir = Path.GetDirectoryName(typeof(object).GetTypeInfo().Assembly.Location);
var mscorlib = MetadataReference.CreateFromFile(Path.Combine(coreDir, "mscorlib.dll"));
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var compilation = CSharpCompilation.Create("MyAssembly")
.AddSyntaxTrees(syntaxTree)
.AddReferences(mscorlib)
.WithOptions(options);
foreach (var d in compilation.GetDiagnostics())
{
Console.WriteLine($"{d.Id}: {d.GetMessage()}");
}
}
}
You'll notice this produces some undesirable diagnostics about missing references and such - the compilation references need to be tweaked a little to include the default libraries (you can see the pattern with mscorlib above). You should see the desired diagnostics about unused using statements as well.

Cannot access Take() on a List<object> with System.Linq referenced in using

I am using RazorEngine for email templating. I have introduced Take() method into the template. I did this so the authors can dictate how many records they want without us having to change any C# in our code directly. I have tried adding the using statements to the template itself as well as using the fluent configuration and adding the namespaces needed but I am not having any luck.
Error:
'System.Collections.Generic.List<object>' does not contain a definition for 'Take'
Here is my fluent configuration for RazorEngine:
var config = new FluentTemplateServiceConfiguration(c =>
c.IncludeNamespaces(
"System",
"System.Linq",
"System.Collections",
"System.Collections.Generic"));
using (var service = new TemplateService(config))
{
//Razor.SetTemplateService(service);
dynamic dyModel = model;
var parsed = string.IsNullOrEmpty(cacheName)
? service.Parse(template, dyModel,null, cacheName)
: service.Parse(template, dyModel,null,null);
return parsed;
}
If I purposely state a namespace incorrectly, I do get an error saying it couldn't find it so I know that it is processing the config data but despite that, I am still getting the error.
Any ideas on what I am doing wrong? I am passing in a dynamic model which either is a List or has a List on it.
So this was kinda lame, there is a ticket on RazorEngine's wiki that states this was fixed, so either I am using it wrong or it hasn't been fixed but this is what I had to do to get it working in the razor file.
var topFive = ((List<dynamic>) Model.MyList).Take(5);

How to get the names of the namespaces loaded at the beginning

How to get the names of the namespaces loaded at the beginning of a C# file? For example, get the six namespace names below.
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Text;
using System.Reflection;
namespace MyNM
{
class MyClass{}
}
This returns all the assemblies references by the executed assembly.
Still, this will NOT return only the namespaces used within a specific file - which is impossible at runtime.
var asms = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
foreach (var referencedAssembly in asms)
{
Console.WriteLine(referencedAssembly.Name);
}
Although, you can, technically, if you know the name of the file which contains your current code, simply read the file at runtime and extract the "using's".
EDIT
The way to do this would be:
public static IEnumerable<string> GetUsings()
{
// Gets the file name of the caller
var fileName = new StackTrace(true).GetFrame(1).GetFileName();
// Get the "using" lines and extract and full namespace
return File.ReadAllLines(fileName)
.Select(line => Regex.Match(line, "^\\s*using ([^;]*)"))
.Where(match => match.Success)
.Select(match => match.Groups[1].Value);
}
How to get the names of the namespaces loaded at the beginning of a C# file? For example, get the six namespace names below.
You can't, other than parsing the file yourself (or using something like the Roslyn CTP to parse a C# file).
Namespaces aren't "loaded" - they are only used by the compile, at compile time, to resolve the appropriate type names.
You can use Assembly.GetReferencedAssemblies to get the assemblies referenced by your assembly (ie: project), but this is an assembly wide set of references, and distinctly different than the namespace using directives included in a specific file.
You have to parse the file to derive the Syntax elements. As mentioned above, you can use System.Reflections for external references or you can use the new Roslyn compiler service as below
string codeSnippet = #"using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Text;
using System.Reflection;
namespace MyNM
{
class MyClass{}
}";
//APPROACH:
//Get using statements in the code snippet from the syntax tree
//find qualified name(eg:System.Text..) in the using directive
SyntaxTree tree = SyntaxTree.ParseCompilationUnit(codeSnippet);
SyntaxNode root = tree.GetRoot();
IEnumerable<UsingDirectiveSyntax> usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>();
foreach(UsingDirectiveSyntax usingDirective in usingDirectives){
NameSyntax ns = usingDirective.Name;
Console.WriteLine(ns.GetText());
}
NOTE: The code uses older version of Roslyn API. It may break in future as Roslyn is still a CTP.

Razor ViewEngine Temporary compilation .cs files

When calling the Parse method in the Razor ViewEngine, compilation errors are thrown as TemplateComplilationException which contains a list of errors. Those errors refer to temporary filenames, but the files are deleted before you can access them.
static void Main(string[] args)
{
var service = TemplateServiceFactory.CreateTemplateService(Language.CSharp, true);
string result = "";
try
{
result = service.Parse("Hello #DateTime.NowXX ");
}
catch (TemplateCompilationException ex)
{
foreach (var error in ex.Errors)
if (!string.IsNullOrEmpty(error.FileName))
Console.WriteLine( File.ReadAllText( error.FileName ));
} // ^^^^ File does not exist!
Console.WriteLine( result );
Console.ReadKey();
}
(a little background)
I'm using the Razor engine "stand-alone" without MVC. When I call the Parse I want to get as much detailed information as possible to display to the user.
The current v2.1 release doesn't provide the ability to spit out the source code. There is a debugging feature in the new v3 codebase that allows the source code to be pushed out. It doesn't do this by default, because I'm trying to make the code as performant as possible (and generating the code twice (once as CodeDom, once as a string) isn't ideal). You'll need to enable the Debug flag on your configuration:
var config = new TemplateServiceConfiguration { Debug = true };
var service = new TemplateService(config);
This will enable the source code to be read when an exception is thrown.
Point of interest, through testing the Roslyn compiler infrastructure with the v3 codebase, it accepts a string source instead of CodeDom, so I'll likely make a future change to use that instead of CodeDom directly - this in turn means we have direct access to the source code without having to worry about enabling any Debug flag which will likely be deprecated.
v3 (currently v3.0.7beta) is available on Nuget (Install-Package RazorEngine). I was aiming to RTW last weekend but never got round to it.
RazorEngine's TemplateCompilationException is a class that wraps a CompilerErrorCollection that contain CompilerError objects, so the most details you could possibly get from the TemplateCompilationException CompilerError objects are their respective properties, which appears to be enough to debug with. Consider and try this example
try
{
Razor.Parse("My erroneous #DateTime.Now.foo()");
}
catch(TemplateCompilationException ex)
{
foreach(var error in ex.Errors)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Compile Error Num: \t" + error.ErrorNumber);
sb.AppendLine("Error Text:\n\t" + error.ErrorText);
Console.WriteLine(sb.ToString());
}
Console.WriteLine("Erroneous Template:\n\t" + ex.Template);
}
When I run my example this is what I get, which tells you the error(s) that was encountered and you can dump the template data to reference for your users.
Compile Error Num: CS1061
Error Text:
'System.DateTime' does not contain a definition for 'foo' and no
extension method 'foo' accepting a first argument of type
'System.DateTime' could be found (are you missing a using directive
or an assembly reference?)
Erroneous Template:
My erroneous #DateTime.Now.foo()

Categories

Resources