Dynamic Objects in WCF not possible? - c#

When building a response in WCF (json), im pretty sure it's not possible to use completely dynamic objects, but just wanted to double check here first.
An ideal response would look something like:
"userTypes" :
{
"BartSimpson" :
{
"url" : "foo",
"desc" : "bar"
},
"LisaSimpson" :
{
"url" : "foo",
"desc" : "bar"
}
}
In 'compiled' code, the above could be performed by the following architecture (slightly pseudocode):
public class Character{
string url {get;set;}
string desc{get;set;}
}
public class UserTypes{
public Character BartSimpson{get;set;}
public Character LisaSimpson{get;set;}
}
But my main goal is that BartSimpson and LisaSimpson are not 'compiled' so I could have any number of Character classes, with any name / identifer in the response.

Add the following using at the top of your service implementation class (make sure that you also add the proper references in your project):
using Newtonsoft.Json;
using System.Dynamic;
using System.IO;
using System.Text;
You may try this simple method which outputs the dynamic result:
public string GetData()
{
dynamic d = new ExpandoObject();
dynamic bartSimpson = new ExpandoObject();
dynamic lisaSimpson = new ExpandoObject();
bartSimpson.url = "foo";
bartSimpson.desc = "bar";
lisaSimpson.url = "foo";
lisaSimpson.desc = "bar";
d.userTypes = new ExpandoObject();
d.userTypes.BartSimpson = bartSimpson;
d.userTypes.LisaSimpson = lisaSimpson;
var s = JsonSerializer.Create();
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
s.Serialize(sw, d);
}
return sb.ToString();
}
To go one more step further (you'll just have to pass Bart and Lisa in the comaSeparatedNames value), you could do:
public string GetData(string comaSeparatedNames)
{
string[] names = comaSeparatedNames.Split(',');
dynamic d = new ExpandoObject();
dynamic dNames = new ExpandoObject();
foreach (var name in names)
{
dynamic properties = new ExpandoObject();
properties.url = "foo";
properties.desc = "bar";
((IDictionary<string, object>)dNames).Add(name, properties);
}
((IDictionary<string, object>)d).Add("userTypes", dNames);
var s = JsonSerializer.Create();
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
s.Serialize(sw, d);
}
// deserializing sample
//dynamic dummy = new ExpandoObject();
//var instance = s.Deserialize(new StringReader(sb.ToString()),
// dummy.GetType());
//var foo = instance.userTypes.BartSimpson.url;
return sb.ToString();
}
Note: I've also provided the lines (commented) for deserialization.

Related

Lucene.Net (4.8) AutoComplete / AutoSuggestion

I'd like to implement a searchable index using Lucene.Net 4.8 that supplies a user with suggestions / autocomplete for single words & phrases.
The index has been created successfully; the suggestions are where I've stalled.
Version 4.8 seems to have introduced a substantial number of breaking changes, and none of the available samples I've found work.
Where I stand
For reference, LuceneVersion is this:
private readonly LuceneVersion LuceneVersion = LuceneVersion.LUCENE_48;
Solution 1
I've tried this, but can't get past reader.Terms:
public void TryAutoComplete()
{
var analyzer = new EnglishAnalyzer(LuceneVersion);
var config = new IndexWriterConfig(LuceneVersion, analyzer);
RAMDirectory dir = new RAMDirectory();
using (IndexWriter iw = new IndexWriter(dir, config))
{
Document d = new Document();
TextField f = new TextField("text","",Field.Store.YES);
d.Add(f);
f.SetStringValue("abc");
iw.AddDocument(d);
f.SetStringValue("colorado");
iw.AddDocument(d);
f.SetStringValue("coloring book");
iw.AddDocument(d);
iw.Commit();
using (IndexReader reader = iw.GetReader(false))
{
TermEnum terms = reader.Terms(new Term("text", "co"));
int maxSuggestsCpt = 0;
// will print:
// colorado
// coloring book
do
{
Console.WriteLine(terms.Term.Text);
maxSuggestsCpt++;
if (maxSuggestsCpt >= 5)
break;
}
while (terms.Next() && terms.Term.Text.StartsWith("co"));
}
}
}
reader.Terms no longer exists. Being new to Lucene, it's unclear how to refactor this.
Solution 2
Trying this, I'm thrown an error:
public void TryAutoComplete2()
{
using(var analyzer = new EnglishAnalyzer(LuceneVersion))
{
IndexWriterConfig config = new IndexWriterConfig(LuceneVersion, analyzer);
RAMDirectory dir = new RAMDirectory();
using(var iw = new IndexWriter(dir,config))
{
Document d = new Document()
{
new TextField("text", "this is a document with a some words",Field.Store.YES),
new Int32Field("id", 42, Field.Store.YES)
};
iw.AddDocument(d);
iw.Commit();
using (IndexReader reader = iw.GetReader(false))
using (SpellChecker speller = new SpellChecker(new RAMDirectory()))
{
//ERROR HERE!!!
speller.IndexDictionary(new LuceneDictionary(reader, "text"), config, false);
string[] suggestions = speller.SuggestSimilar("dcument", 5);
IndexSearcher searcher = new IndexSearcher(reader);
foreach (string suggestion in suggestions)
{
TopDocs docs = searcher.Search(new TermQuery(new Term("text", suggestion)), null, Int32.MaxValue);
foreach (var doc in docs.ScoreDocs)
{
System.Diagnostics.Debug.WriteLine(searcher.Doc(doc.Doc).Get("id"));
}
}
}
}
}
}
When debugging, speller.IndexDictionary(new LuceneDictionary(reader, "text"), config, false); throws a The object cannot be set twice! error, which I can't explain.
Any thoughts are welcome.
Clarification
I'd like to return a list of suggested terms for a given input, not the documents or their full content.
For example, if a document contains "Hello, my name is Clark. I'm from Atlanta," and I submit "Atl," then "Atlanta" should come back as a suggestion.
If I am understanding you correctly you may be over-complicating your index design a bit. If your goal is to use Lucene for auto-complete, you want to create an index of the terms you consider complete. Then simply query the index using a PrefixQuery using a partial word or phrase.
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.En;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;
using System;
using System.Linq;
namespace LuceneDemoApp
{
class LuceneAutoCompleteIndex : IDisposable
{
const LuceneVersion Version = LuceneVersion.LUCENE_48;
RAMDirectory Directory;
Analyzer Analyzer;
IndexWriterConfig WriterConfig;
private void IndexDoc(IndexWriter writer, string term)
{
Document doc = new Document();
doc.Add(new StringField(FieldName, term, Field.Store.YES));
writer.AddDocument(doc);
}
public LuceneAutoCompleteIndex(string fieldName, int maxResults)
{
FieldName = fieldName;
MaxResults = maxResults;
Directory = new RAMDirectory();
Analyzer = new EnglishAnalyzer(Version);
WriterConfig = new IndexWriterConfig(Version, Analyzer);
WriterConfig.OpenMode = OpenMode.CREATE_OR_APPEND;
}
public string FieldName { get; }
public int MaxResults { get; set; }
public void Add(string term)
{
using (var writer = new IndexWriter(Directory, WriterConfig))
{
IndexDoc(writer, term);
}
}
public void AddRange(string[] terms)
{
using (var writer = new IndexWriter(Directory, WriterConfig))
{
foreach (string term in terms)
{
IndexDoc(writer, term);
}
}
}
public string[] WhereStartsWith(string term)
{
using (var reader = DirectoryReader.Open(Directory))
{
IndexSearcher searcher = new IndexSearcher(reader);
var query = new PrefixQuery(new Term(FieldName, term));
TopDocs foundDocs = searcher.Search(query, MaxResults);
var matches = foundDocs.ScoreDocs
.Select(scoreDoc => searcher.Doc(scoreDoc.Doc).Get(FieldName))
.ToArray();
return matches;
}
}
public void Dispose()
{
Directory.Dispose();
Analyzer.Dispose();
}
}
}
Running this:
var indexValues = new string[] { "apple fruit", "appricot", "ape", "avacado", "banana", "pear" };
var index = new LuceneAutoCompleteIndex("fn", 10);
index.AddRange(indexValues);
var matches = index.WhereStartsWith("app");
foreach (var match in matches)
{
Console.WriteLine(match);
}
You get this:
apple fruit
appricot

Adding dynamic object to json

first of all, thanks for reading. Furthermore, i need your help!
By the way, im using Newtonsoft.Json;
How can i add a string to this:
obj.namedTypes.Menu.fields.< string > = new ExpandoObject();
So i have this code at the moment:
internal class Program
{
private static void Main(string[] args)
{
Regex reg = new Regex(":addSubMenu\\((?<Description>.+),[a-z A-Z](\'|\")(?<Identifier>[a-zA-Z]+)(\'|\")\\)"); //Regular expression for finding the id and description i want.
dynamic obj = new ExpandoObject();
obj.global = new ExpandoObject();
obj.global.type = "table";
obj.global.fields = new ExpandoObject();
obj.global.fields.scriptConfig = new ExpandoObject();
obj.global.fields.scriptConfig.type = "function";
obj.global.fields.scriptConfig.args = new string[] {};
obj.global.fields.scriptConfig.description = "a simple description";
obj.global.fields.scriptConfig.returnTypes = new List<ExpandoObject>();
dynamic arguments = new ExpandoObject();
arguments.type = "ref";
arguments.name = "Menu";
obj.global.fields.scriptConfig.returnTypes.Add(arguments);
obj.namedTypes = new ExpandoObject();
obj.namedTypes.Menu = new ExpandoObject();
obj.namedTypes.Menu.type = "table";
obj.namedTypes.Menu.fields = new ExpandoObject();
Console.WriteLine(JsonConvert.SerializeObject(obj, Formatting.Indented));
MatchCollection matches = reg.Matches(File.ReadAllText("test.txt"));
string description;
string value;
foreach (Match m in matches)
{
description = m.Groups["Description"].ToString();
value = m.Groups["Identifier"].ToString();
/* I want to add the value as a object like this: ->
* obj.namedTypes.Menu.fields.<value> = new ExpandoObject();
* obj.namedTypes.Menu.fields.<value>.description = description;
*/
Console.WriteLine($"Description: {description}\nValue: {value}");
}
Console.ReadLine();
}
I want to add the value as a object like this: ->
obj.namedTypes.Menu.fields.<value> = new ExpandoObject();
obj.namedTypes.Menu.fields.<value>.description = description;
Try using Dictionary.
Your modified code would be -
//Initialization of Dictonary
obj.namedTypes.Menu.fields = new Dictionary<string,string>();
//Reading from File and iteration on matches
MatchCollection matches = reg.Matches(File.ReadAllText("test.txt"));
string description;
string value;
foreach (Match m in matches)
{
description = m.Groups["Description"].ToString();
value = m.Groups["Identifier"].ToString();
obj.namedTypes.Menu.fields.Add(description , value);
}
//End: Serialize the Whole stuff
Console.WriteLine(JsonConvert.SerializeObject(obj, Formatting.Indented));

How to generate mdx query using C#?

I am new to mdx query and I am very curious about mdx query generation using C# so I searched for any demo or open source then I found Ranet.olap (https://ranetuilibraryolap.codeplex.com/) which is providing what I need.
After taking the dlls I tried to incorporate them in my code. I am pasting my full console code which should generate mdx query but it's not doing so, am I doing something wrong?
using System;
using System.Collections.Generic;
using Microsoft.AnalysisServices.AdomdClient;
using Ranet.Olap.Core.Managers;
using Ranet.Olap.Core.Metadata;
using Ranet.Olap.Core.Types;
namespace MDX
{
class Program
{
static void Main(string[] args)
{
startWork();
}
public static void startWork()
{
string connString = "Provider=MSOLAP.3; Data Source=localhost;Initial Catalog=AdventureWorkDW2008R2;Integrated Security=SSPI;";
CubeDef cubes;
AdomdConnection conn = new AdomdConnection(connString);
conn.Open();
cubes = conn.Cubes.Find("AdventureWorkCube");
Ranet.Olap.Core.Managers.MdxQueryBuilder mdx = new Ranet.Olap.Core.Managers.MdxQueryBuilder();
mdx.Cube = cubes.Caption;
List<Ranet.Olap.Core.Wrappers.AreaItemWrapper> listColumn = new List<Ranet.Olap.Core.Wrappers.AreaItemWrapper>();
List<Ranet.Olap.Core.Wrappers.AreaItemWrapper> listRow = new List<Ranet.Olap.Core.Wrappers.AreaItemWrapper>();
List<Ranet.Olap.Core.Wrappers.AreaItemWrapper> listData = new List<Ranet.Olap.Core.Wrappers.AreaItemWrapper>();
//Column area
Dimension dmColumn = cubes.Dimensions.Find("Dim Product");
Microsoft.AnalysisServices.AdomdClient.Hierarchy hColumn = dmColumn.Hierarchies["English Product Name"];
//hierarchy properties
List<PropertyInfo> lPropInfo = new List<PropertyInfo>();
foreach (var prop in hColumn.Properties)
{
PropertyInfo p = new PropertyInfo();
p.Name = prop.Name;
p.Value = prop.Value;
lPropInfo.Add(p);
}
Ranet.Olap.Core.Wrappers.AreaItemWrapper areaIColumn = new Ranet.Olap.Core.Wrappers.AreaItemWrapper();
areaIColumn.AreaItemType = AreaItemWrapperType.Hierarchy_AreaItemWrapper;
areaIColumn.Caption = hColumn.Caption;
areaIColumn.CustomProperties = lPropInfo;
listColumn.Add(areaIColumn);
//Rows Area
Dimension dmRow = cubes.Dimensions.Find("Due Date");
Microsoft.AnalysisServices.AdomdClient.Hierarchy hRow = dmRow.Hierarchies["English Month Name"];
List<PropertyInfo> lRowPropInfo = new List<PropertyInfo>();
foreach (var prop in hRow.Properties)
{
PropertyInfo p = new PropertyInfo(prop.Name,prop.Value);
lRowPropInfo.Add(p);
}
Ranet.Olap.Core.Wrappers.AreaItemWrapper areaIRow = new Ranet.Olap.Core.Wrappers.AreaItemWrapper();
areaIRow.AreaItemType = AreaItemWrapperType.Hierarchy_AreaItemWrapper;
areaIRow.Caption = hRow.Caption;
areaIRow.CustomProperties = lRowPropInfo;
listRow.Add(areaIRow);
//Measure Area or Data Area
Measure ms = cubes.Measures.Find("Order Quantity");
Ranet.Olap.Core.Wrappers.AreaItemWrapper areaIData = new Ranet.Olap.Core.Wrappers.AreaItemWrapper();
areaIData.AreaItemType = AreaItemWrapperType.Measure_AreaItemWrapper;
areaIData.Caption = ms.Caption;
List<PropertyInfo> lmpropInfo = new List<PropertyInfo>();
foreach (var prop in ms.Properties)
{
PropertyInfo p = new PropertyInfo(prop.Name, prop.Value);
lmpropInfo.Add(p);
}
areaIData.CustomProperties = lmpropInfo;
listData.Add(areaIData);
mdx.AreaWrappersColumns = listColumn;
mdx.AreaWrappersRows = listRow;
mdx.AreaWrappersData = listData;
string mdxQuery = mdx.GenerateMdxQuery();
conn.Close();
}
}
}
A simple example of the generation mdx query (only Ranet OLAP 3.7 version):
using System.Collections.Generic;
using Ranet.Olap.Core.Data;
using Ranet.Olap.Core.Managers;
using Ranet.Olap.Core.Types;
using Ranet.Olap.Core.Wrappers;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
startWork();
}
public static void startWork()
{
var mdx = new QueryBuilderParameters
{
CubeName = "[Adventure Works]",
SubCube = "",
MdxDesignerSetting = new MDXDesignerSettingWrapper(),
CalculatedMembers = new List<CalcMemberInfo>(),
CalculatedNamedSets = new List<CalculatedNamedSetInfo>(),
AreaWrappersFilter = new List<AreaItemWrapper>(),
AreaWrappersColumns = new List<AreaItemWrapper>(),
AreaWrappersRows = new List<AreaItemWrapper>(),
AreaWrappersData = new List<AreaItemWrapper>()
};
//define parameters
mdx.MdxDesignerSetting.HideEmptyColumns = false;
mdx.MdxDesignerSetting.HideEmptyRows = false;
mdx.MdxDesignerSetting.UseVisualTotals = false;
mdx.MdxDesignerSetting.SubsetCount = 0;
var itemCol1 = new Hierarchy_AreaItemWrapper
{
AreaItemType = AreaItemWrapperType.Hierarchy_AreaItemWrapper,
UniqueName = "[Customer].[Customer Geography]"
};
mdx.AreaWrappersColumns.Add(itemCol1);
var itemRow1 = new Hierarchy_AreaItemWrapper
{
AreaItemType = AreaItemWrapperType.Hierarchy_AreaItemWrapper,
UniqueName = "[Date].[Calendar]"
};
mdx.AreaWrappersRows.Add(itemRow1);
var itemData1 = new Measure_AreaItemWrapper();
itemData1.AreaItemType = AreaItemWrapperType.Measure_AreaItemWrapper;
itemData1.UniqueName = "[Measures].[Internet Order Count]";
mdx.AreaWrappersData.Add(itemData1);
string query = MdxQueryBuilder.Default.BuildQuery(mdx, null);
}
}
}
MDX Query result:
SELECT
HIERARCHIZE(HIERARCHIZE([Customer].[Customer Geography].Levels(0).Members)) DIMENSION PROPERTIES PARENT_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, CUSTOM_ROLLUP, UNARY_OPERATOR, KEY0 ON 0,
HIERARCHIZE(HIERARCHIZE([Date].[Calendar].Levels(0).Members)) DIMENSION PROPERTIES PARENT_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, CUSTOM_ROLLUP, UNARY_OPERATOR, KEY0 ON 1
FROM
[Adventure Works]
WHERE ([Measures].[Internet Order Count])
CELL PROPERTIES BACK_COLOR, CELL_ORDINAL, FORE_COLOR, FONT_NAME, FONT_SIZE, FONT_FLAGS, FORMAT_STRING, VALUE, FORMATTED_VALUE, UPDATEABLE, ACTION_TYPE
Still in process of revising code for this engine, though some suggestions for you:
It looks like you just grab cube metadata (dims, measures etc.) and pass it to generator. This does not sound like a way to generate MDX. MDX statement should look like
select
{
// measures, calculated members
} on 0,
{
// dimension data - sets
} on 1 // probably more axis
from **Cube**
All other parameters are optional

Adding an Array Field using FileHelpers DelimitedClassBuilder

How do I do this dynamically using DelimitedClassBuilder so that the columns in the file can expand but not break my program?
[DelimitedRecord(",")]
public class MyRecord
{
public string Name;
[FieldOptional, FieldArrayLength(0, 100)]
public string[] I_DONT_CARE_WHAT_COMES_AFTER_THIS;
}
i.e. how do I finish this:
var cb = new DelimitedClassBuilder("xyz", ",");
cb.AddField("Name", "string");
... how do I add the array field here?
Type type = cb.CreateRecordClass();
var engine = new DelimitedFileEngine(type);
Good question. The best I can find is:
var cb = new DelimitedClassBuilder("xyz", ",");
cb.AddField("Name", "string");
cb.AddFields(100);
foreach (var field in cb.Fields.Where(f => f.FieldName.StartsWith("Field")))
{
field.FieldOptional = true;
}
var type = cb.CreateRecordClass();
var engine = new DelimitedFileEngine(type);
I can't get it to work with
cb.AddField("I_DONT_CARE", typeof(string[]));
Nor with
cb.AddField("I_DONT_CARE", typeof(string[]).FullName);
both of which ought to work.

Create Json dynamically in c#

I need to create a Json object dynamically by looping through columns.
so declaring an empty json object then add elements to it dynamically.
eg:
List<String> columns = new List<String>{"FirstName","LastName"};
var jsonObj = new {};
for(Int32 i=0;i<columns.Count();i++)
jsonObj[col[i]]="Json" + i;
And the final json object should be like this:
jsonObj={FirstName="Json0", LastName="Json1"};
[TestFixture]
public class DynamicJson
{
[Test]
public void Test()
{
dynamic flexible = new ExpandoObject();
flexible.Int = 3;
flexible.String = "hi";
var dictionary = (IDictionary<string, object>)flexible;
dictionary.Add("Bool", false);
var serialized = JsonConvert.SerializeObject(dictionary); // {"Int":3,"String":"hi","Bool":false}
}
}
I found a solution very similar to DPeden, though there is no need to use the IDictionary, you can pass directly from an ExpandoObject to a JSON convert:
dynamic foo = new ExpandoObject();
foo.Bar = "something";
foo.Test = true;
string json = Newtonsoft.Json.JsonConvert.SerializeObject(foo);
and the output becomes:
{ "FirstName":"John", "LastName":"Doe", "Active":true }
You should use the JavaScriptSerializer. That can Serialize actual types for you into JSON :)
Reference: http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx
EDIT: Something like this?
var columns = new Dictionary<string, string>
{
{ "FirstName", "Mathew"},
{ "Surname", "Thompson"},
{ "Gender", "Male"},
{ "SerializeMe", "GoOnThen"}
};
var jsSerializer = new JavaScriptSerializer();
var serialized = jsSerializer.Serialize(columns);
Output:
{"FirstName":"Mathew","Surname":"Thompson","Gender":"Male","SerializeMe":"GoOnThen"}
Using dynamic and JObject
dynamic product = new JObject();
product.ProductName = "Elbow Grease";
product.Enabled = true;
product.StockCount = 9000;
Console.WriteLine(product.ToString());
// {
// "ProductName": "Elbow Grease",
// "Enabled": true,
// "StockCount": 9000
// }
Or how about:
JObject obj = JObject.FromObject(new
{
ProductName = "Elbow Grease",
Enabled = true,
StockCount = 9000
});
Console.WriteLine(obj.ToString());
// {
// "ProductName": "Elbow Grease",
// "Enabled": true,
// "StockCount": 9000
// }
https://www.newtonsoft.com/json/help/html/CreateJsonDynamic.htm

Categories

Resources