Currently I am working on simple code analyse for c# with roslyn. I need to parse all document of all projects inside one solution and getting the declared used classes inside this document.
For example from:
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
}
}
I want to get Program uses Foo.
I already parse all documents and get the declared class inside.
// all projects in solution
foreach (var project in _solution.Projects)
{
// all documents inside project
foreach (var document in project.Documents)
{
var syntaxRoot = await document.GetSyntaxRootAsync();
var model = await document.GetSemanticModelAsync();
var classes = syntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>();
// all classes inside document
foreach (var classDeclarationSyntax in classes)
{
var symbol = model.GetDeclaredSymbol(classDeclarationSyntax);
var objectCreationExpressionSyntaxs = classDeclarationSyntax.DescendantNodes().OfType<ObjectCreationExpressionSyntax>();
// all object creations inside document
foreach (var objectCreationExpressionSyntax in objectCreationExpressionSyntaxs)
{
// TODO: Get the identifier value
}
}
}
}
The problem is to get the IdentifierName Foo. Using the debugger, I see objectCreationExpressionSyntax.Typegot the Identifier.Text got the value I need, but objectCreationExpressionSyntax.Type.Identifierseems to be private.
I could use the SymbolFinder to find all references of a Class in the solution. As I already need to parse all documents its should work without.
Maybe I am on the wrong path? How to get the identifier value?
You'll need to handle the different types of TypeSyntaxes. See here: http://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Syntax/TypeSyntax.cs,29171ac4ad60a546,references
What you see in the debugger is a SimpleNameSyntax, which does have a public Identifier property.
Update
var ns = objectCreationExpressionSyntax.Type as NameSyntax;
if (ns != null)
{
return ns.Identifier.ToString();
}
var pts = objectCreationExpressionSyntax.Type as PredefinedTypeSyntax;
if (pts != null)
{
return pts.Keyword.ToString();
}
...
All other subtypes would need to be handed. Note that ArrayType.ElementType is also a TypeSyntax, so you would most probably need to make this method recursive.
You can get the identifier from the syntax's Type property:
foreach (var objectCreationExpressionSyntax in objectCreationExpressionSyntaxs)
{
IdentifierNameSyntax ins = (IdentifierNameSyntax)objectCreationExpressionSyntax.Type;
var id = ins.Identifier;
Console.WriteLine(id.ValueText);
}
Strings can be misleading.
Let's say you have the expression new SomeClass(), and you get the string "SomeClass" out of it. How do you know if that refers to Namespace1.SomeClass or Namespace2.SomeClass ? What if there is a using SomeClass = Namespace3.SomeOtherType; declaration being used?
Fortunately, you don't have to do this analysis yourself. The compiler can bind the ObjectCreationExpressionSyntax to a symbol. You have your semantic model, use it.
foreach (var oce in objectCreationExpressionSyntaxs)
{
ITypeSymbol typeSymbol = model.GetTypeInfo(oce).Type;
// ...
}
You can compare this symbol with the symbols you get from model.GetDeclaredSymbol(classDeclarationSyntax), just make sure you use the Equals method, not the == operator.
I work with Linq xml in C#, but I don't know how to correct processing exception, when some elements is null.
For example, I need get some value of attribute, but this attribute can be null, or can be null some part of path. I do like this:
public static string GetImage(this HtmlNode element)
{
var result = "";
try
{
return result = element.Element("div").Element("a").Element("img")?.GetAttribute("src").Value;
}
catch (Exception)
{
return result;
}
}
Maybe I can do it easy?
Thanks for answer.
If you already are using C# 6.0, then use null conditional operators as you already did after Element("img"):
return element?.Element("div")?.Element("a")?.Element("img")?.GetAttribute("src")?.Value;
I am currently in the process of rewriting my XmlDocument and XmlElements to XDocument and XElement. I've run into a small problem, I used to simply select a single node ModuleRequests and than loop through it. I thought simple right I'll just rewrite this:
var propertiesRequested = new XmlDocument();
propertiesRequested.LoadXml(propertiesConfiguration);
var requests = propertiesRequested.SelectSingleNode("ModuleRequests");
foreach (XmlNode request in requests)
{
var propertyValue = request.Attributes["Id"].Value;
if (systemProperties.ContainsKey(propertyValue) == false)
{
systemProperties.Add(propertyValue, request.Attributes["Request"].Value);
}
}
To this:
var propertiesRequested = XDocument.Parse(propertiesConfiguration);
var requests = propertiesRequested.Element("ModuleRequests");
foreach (XNode request in requests)
{
var propertyValue = request.Attributes["Id"].Value;
if (systemProperties.ContainsKey(propertyValue) == false)
{
systemProperties.Add(propertyValue, request.Attributes["Request"].Value);
}
}
Well needless to say it isn't that easy, then I thought fine i'll make it:
foreach(XNode request in requests.Nodes())
but this gave me even more problems since an XNode does not have an attribute.
As you can probably tell I'm a bit of a novice when it comes to xml reading. I'm hoping someone can help me out. What is the correct way to rewrite from XmlDocument to XDocument
You want to use XElement.Elements() to iterate through all child elements of your requests element, then use XElement.Attribute(XName name) to fetch the specified attribute by name.
You might also consider explicitly casting your XAttribute to a string rather than using the Value property, as the former will return null on a missing attribute rather than generating a null reference exception.
Thus:
var propertiesRequested = XDocument.Parse(propertiesConfiguration);
var requests = propertiesRequested.Element("ModuleRequests");
foreach (var request in requests.Elements())
{
var propertyValue = (string)request.Attribute("Id");
if (systemProperties.ContainsKey(propertyValue) == false)
{
systemProperties.Add(propertyValue, (string)request.Attribute("Request"));
}
}
I am trying to read multiple attributes from an xml file using XMLNode, but depending on the element, the attribute might not exist. In the event the attribute does not exist, if I try to read it into memory, it will throw a null exception. I found one way to test if the attribute returns null:
var temp = xn.Attributes["name"].Value;
if (temp == null)
{ txtbxName.Text = ""; }
else
{ txtbxName.Text = temp; }
This seems like it will work for a single instance, but if I am checking 20 attributes that might not exist, I'm hoping there is a way to setup a method I can pass the value to test if it is null. From what I have read you can't pass a var as it is locally initialized, but is there a way I could setup a test to pass a potentially null value to be tested, then return the value if it is not null, and return "" if it is null? Is it possible, or do would I have to test each value individually as outlined above?
You can create a method like this:
public static string GetText(XmlNode xn, string attrName)
{
var attr = xn.Attributes[attrName];
if (attr == null). // Also check whether the attribute does not exist at all
return string.Empty;
var temp = attr.Value;
if (temp == null)
return string.Empty;
return temp;
}
And call it like this:
txtbxName.Text = GetText(xn, "name");
If you use an XDocument you could just use Linq to find all the nodes you want.
var names = (from attr in doc.Document.Descendants().Attributes()
where attr.Name == "name"
select attr).ToList();
If you are using XmlDocument for some reason, you could select the nodes you want using XPath. (My XPath is rusty).
var doc = new XmlDocument();
doc.Load("the file");
var names = doc.SelectNodes("//[Name=\"name\"");
So for viewing a current object's state at runtime, I really like what the Visual Studio Immediate window gives me. Just doing a simple
? objectname
Will give me a nicely formatted 'dump' of the object.
Is there an easy way to do this in code, so I can do something similar when logging?
For a larger object graph, I second the use of Json but with a slightly different strategy. First I have a static class that is easy to call and with a static method that wraps the Json conversion (note: could make this an extension method).
using Newtonsoft.Json;
public static class F
{
public static string Dump(object obj)
{
return JsonConvert.SerializeObject(obj);
}
}
Then in your Immediate Window,
var lookHere = F.Dump(myobj);
lookHere will auto-show up in the Locals window prepended with a $ or you can add a watch to it. On the right hand side of the Value column in the inspector, there is a magnifying glass with a dropdown caret beside it. Choose the dropdown caret and choose Json visualizer.
I am using Visual Studio 2013.
You could base something on the ObjectDumper code that ships with the Linq samples.
Have also a look at the answer of this related question to get a sample.
You could use Visual Studio Immediate Window
Just paste this (change actual to your object name obviously):
Newtonsoft.Json.JsonConvert.SerializeObject(actual);
It should print object in JSON
You should be able to copy it over textmechanic text tool or notepad++ and replace escaped quotes (\") with " and newlines (\r\n) with empty space, then remove double quotes (") from beginning and end and paste it to jsbeautifier to make it more readable.
UPDATE to OP's comment
public static class Dumper
{
public static void Dump(this object obj)
{
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj)); // your logger
}
}
this should allow you to dump any object.
Hope this saves you some time.
I'm certain there are better ways of doing this, but I have in the past used a method something like the following to serialize an object into a string that I can log:
private string ObjectToXml(object output)
{
string objectAsXmlString;
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(output.GetType());
using (System.IO.StringWriter sw = new System.IO.StringWriter())
{
try
{
xs.Serialize(sw, output);
objectAsXmlString = sw.ToString();
}
catch (Exception ex)
{
objectAsXmlString = ex.ToString();
}
}
return objectAsXmlString;
}
You'll see that the method might also return the exception rather than the serialized object, so you'll want to ensure that the objects you want to log are serializable.
ServiceStack.Text has a T.Dump() extension method that does exactly this, recursively dumps all properties of any type in a nice readable format.
Example usage:
var model = new TestModel();
Console.WriteLine(model.Dump());
and output:
{
Int: 1,
String: One,
DateTime: 2010-04-11,
Guid: c050437f6fcd46be9b2d0806a0860b3e,
EmptyIntList: [],
IntList:
[
1,
2,
3
],
StringList:
[
one,
two,
three
],
StringIntMap:
{
a: 1,
b: 2,
c: 3
}
}
Here is a stupidly simple way to write a flat object, nicely formatted:
using Newtonsoft.Json.Linq;
Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());
What's going on is that the object is first converted to a JSON internal representation by JObject.FromObject, and then converted to JSON string by ToString. (And of course a JSON string is a very nice representation of a simple object, especially since ToString will include newlines and indents.) The "ToString" is of course extraneous (as it's implied by using + to concat a string and an object), but I kinda like to specify it here.
You could use reflection and loop through all the object properties, then get their values and save them to the log. The formatting is really trivial (you could use \t to indent an objects properties and its values):
MyObject
Property1 = value
Property2 = value2
OtherObject
OtherProperty = value ...
What I like doing is overriding ToString() so that I get more useful output beyond the type name. This is handy in the debugger, you can see the information you want about an object without needing to expand it.
Following is another version that does the same thing (and handle nested properties), which I think is simpler (no dependencies on external libraries and can be modified easily to do things other than logging):
public class ObjectDumper
{
public static string Dump(object obj)
{
return new ObjectDumper().DumpObject(obj);
}
StringBuilder _dumpBuilder = new StringBuilder();
string DumpObject(object obj)
{
DumpObject(obj, 0);
return _dumpBuilder.ToString();
}
void DumpObject(object obj, int nestingLevel = 0)
{
var nestingSpaces = "".PadLeft(nestingLevel * 4);
if (obj == null)
{
_dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
}
else if (obj is string || obj.GetType().IsPrimitive)
{
_dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
}
else if (ImplementsDictionary(obj.GetType()))
{
using (var e = ((dynamic)obj).GetEnumerator())
{
var enumerator = (IEnumerator)e;
while (enumerator.MoveNext())
{
dynamic p = enumerator.Current;
var key = p.Key;
var value = p.Value;
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
DumpObject(value, nestingLevel + 1);
}
}
}
else if (obj is IEnumerable)
{
foreach (dynamic p in obj as IEnumerable)
{
DumpObject(p, nestingLevel);
}
}
else
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
{
string name = descriptor.Name;
object value = descriptor.GetValue(obj);
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
DumpObject(value, nestingLevel + 1);
}
}
}
bool ImplementsDictionary(Type t)
{
return t.GetInterfaces().Any(i => i.Name.Contains("IDictionary"));
}
}
I found a library called ObjectPrinter which allows to easily dump objects and collections to strings (and more). It does exactly what I needed.
You can write your own WriteLine method-
public static void WriteLine<T>(T obj)
{
var t = typeof(T);
var props = t.GetProperties();
StringBuilder sb = new StringBuilder();
foreach (var item in props)
{
sb.Append($"{item.Name}:{item.GetValue(obj,null)}; ");
}
sb.AppendLine();
Console.WriteLine(sb.ToString());
}
Use it like-
WriteLine(myObject);
To write a collection we can use-
var ifaces = t.GetInterfaces();
if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
{
dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
while (lst.MoveNext())
{
WriteLine(lst.Current);
}
}
The method may look like-
public static void WriteLine<T>(T obj)
{
var t = typeof(T);
var ifaces = t.GetInterfaces();
if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
{
dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
while (lst.MoveNext())
{
WriteLine(lst.Current);
}
}
else if (t.GetProperties().Any())
{
var props = t.GetProperties();
StringBuilder sb = new StringBuilder();
foreach (var item in props)
{
sb.Append($"{item.Name}:{item.GetValue(obj, null)}; ");
}
sb.AppendLine();
Console.WriteLine(sb.ToString());
}
}
Using if, else if and checking interfaces, attributes, base type, etc. and recursion (as this is a recursive method) in this way we may achieve an object dumper, but it is tedious for sure. Using the object dumper from Microsoft's LINQ Sample would save your time.
All of the paths above assume that your objects are serializable to XML or JSON,
or you must implement your own solution.
But in the end you still get to the point where you have to solve problems like
recursion in objects
non-serializable objects
exceptions
...
Plus log you want more information:
when the event happened
callstack
which threead
what was in the web session
which ip address
url
...
There is the best solution that solves all of this and much more.
Use this Nuget package: Desharp.
For all types of applications - both web and desktop applications.
See it's Desharp Github documentation. It has many configuration options.
Just call anywhere:
Desharp.Debug.Log(anyException);
Desharp.Debug.Log(anyCustomValueObject);
Desharp.Debug.Log(anyNonserializableObject);
Desharp.Debug.Log(anyFunc);
Desharp.Debug.Log(anyFunc, Desharp.Level.EMERGENCY); // you can store into different files
it can save the log in nice HTML (or in TEXT format, configurable)
it's possible to write optionally in background thread (configurable)
it has options for max objects depth and max strings length (configurable)
it uses loops for iteratable objects and backward reflection for everything else,
indeed for anything you can find in .NET environment.
I believe it will help.
So far a simplest and tidiest way for me is a serializer from YamlDotNet package.
using YamlDotNet.Serialization;
List<string> strings=new List<string>{"a","b","c"};
new Serializer().Serialize(strings)
will give you
- a
- b
- c
A more comprehensive example is here https://dotnetfiddle.net/KuV63n
Today you don't even need an external dependency. You can just use the built-in Microsoft Json Serializer.
using System;
using System.Text.Json;
namespace MyCompany.Core.Extensions
{
public static class ObjectExtensions
{
public static string Dump(this object obj)
{
try
{
return JsonSerializer.Serialize(obj);
}
catch(Exception)
{
return string.Empty;
}
}
}
}
Notice that you can pass a JsonSerializerOptions parameter to further customize the serialization to your liking:
Let's say you want to write the JSON indented for easy reading... we'd use:
new JsonSerializerOptions { WriteIndented = true }
#######
Here's a good guide if you wish to migrate from NewtonSoft.Json to System.Text.Json:
Compare Newtonsoft.Json to System.Text.Json, and migrate to System.Text.Json
Based on #engineforce answer, I made this class that I'm using in a PCL project of a Xamarin Solution:
/// <summary>
/// Based on: https://stackoverflow.com/a/42264037/6155481
/// </summary>
public class ObjectDumper
{
public static string Dump(object obj)
{
return new ObjectDumper().DumpObject(obj);
}
StringBuilder _dumpBuilder = new StringBuilder();
string DumpObject(object obj)
{
DumpObject(obj, 0);
return _dumpBuilder.ToString();
}
void DumpObject(object obj, int nestingLevel)
{
var nestingSpaces = "".PadLeft(nestingLevel * 4);
if (obj == null)
{
_dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
}
else if (obj is string || obj.GetType().GetTypeInfo().IsPrimitive || obj.GetType().GetTypeInfo().IsEnum)
{
_dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
}
else if (ImplementsDictionary(obj.GetType()))
{
using (var e = ((dynamic)obj).GetEnumerator())
{
var enumerator = (IEnumerator)e;
while (enumerator.MoveNext())
{
dynamic p = enumerator.Current;
var key = p.Key;
var value = p.Value;
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
DumpObject(value, nestingLevel + 1);
}
}
}
else if (obj is IEnumerable)
{
foreach (dynamic p in obj as IEnumerable)
{
DumpObject(p, nestingLevel);
}
}
else
{
foreach (PropertyInfo descriptor in obj.GetType().GetRuntimeProperties())
{
string name = descriptor.Name;
object value = descriptor.GetValue(obj);
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
// TODO: Prevent recursion due to circular reference
if (name == "Self" && HasBaseType(obj.GetType(), "NSObject"))
{
// In ObjC I need to break the recursion when I find the Self property
// otherwise it will be an infinite recursion
Console.WriteLine($"Found Self! {obj.GetType()}");
}
else
{
DumpObject(value, nestingLevel + 1);
}
}
}
}
bool HasBaseType(Type type, string baseTypeName)
{
if (type == null) return false;
string typeName = type.Name;
if (baseTypeName == typeName) return true;
return HasBaseType(type.GetTypeInfo().BaseType, baseTypeName);
}
bool ImplementsDictionary(Type t)
{
return t is IDictionary;
}
}
Option 1: CSharpObjectFormatter from the Microsoft.CodeAnalysis.CSharp.Scripting.
C# REPL Command-Line Interface (CSI.EXE) and CSharpRepl use exactly this formatter.
using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
var students = new[]
{
new Student
{
Id = 1,
Name = "Michael Hilus"
},
new Student
{
Id = 2,
Name = "Alicia Keys"
}
};
var formattedStudents = CSharpObjectFormatter.Instance.FormatObject(students);
Console.WriteLine(formattedStudents);
Output:
Student[2] { Student { Id=1, Name="Michael Hilus" }, Student { Id=2, Name="Alicia Keys" } }
But it's suitable for relatively simple objects, for example it can't dump the instance of System.Data.DataTable.
Option 2: vm.Aspects.Diagnostics.ObjectTextDumper - see documentation.
There is also very popular library:
ObjectDumper.NET, it's an open source, but not free for commercial use.