I have a block of code that works in .NET 4.0+, but I need to use this code in an SSIS package that only supports up to .NET 3.5. The problem is I can't use the dynamic object below 4.0. I'm unable to find a workaround, any ideas?
string json = File.ReadAllText(#"C:json.txt");
dynamic deserialisedJson = JsonConvert.DeserializeObject(json);
var locations = new List<Location>();
foreach (var root in deserialisedJson)
{
foreach (var state in root)
{
foreach (var city in state)
{
foreach (var location in city)
{
Location loc = new Location();
loc.CafeId = location.First["cafeID"];
loc.CafeName = location.First["cafeName"];
loc.CafeState = location.First["cafeState"];
loc.CafeCity = location.First["cafeCity"];
loc.CafeStreetName = location.First["cafeStreetName"];
loc.CafeZip = location.First["cafeZip"];
locations.Add(loc);
}
}
}
}
UPDATE
Adding JSON schema
{
"AK": {
"Anchorage": [{
"Name": "John Doe",
"Address": "123 Main St.",
"City": "Anchorage",
"State": "AK",
"Zip": "12345"
}],
"Fairbanks": [{
"Name": "Sally Smith",
"Address": "987 Main St.",
"City": "Fairbanks",
"State": "AK",
"Zip": "98765"
}]
}
}
UPDATE 2
I am attempting the IEnumerable workaround, but not sure what the correct syntax is so that I can grab the values I need:
string json = File.ReadAllText(#"C:json.txt");
var deserialisedJson = (IEnumerable)JsonConvert.DeserializeObject(json);
var locations = new List<Location>();
foreach (var root in deserialisedJson)
{
foreach (var state in (IEnumerable)root)
{
foreach (var city in (IEnumerable)state)
{
foreach (var location in (IEnumerable)city)
{
Location loc = new Location();
loc.Name = //What goes here???
loc.Address = //What goes here???
loc.City = //What goes here???
loc.State = //What goes here???
loc.Zip = //What goes here???
locations.Add(loc);
}
}
}
}
from another post - Newtonsoft JSON Deserialize
class MyData
{
public string t;
public bool a;
public object[] data;
public string[][] type;
}
and then use the generic version of DeserializeObject:
MyData tmp = JsonConvert.DeserializeObject<MyData>(json);
foreach (string typeStr in tmp.type[0])
{
// Do something with typeStr
}
Without an example json I can only speculate - but it looks like you know the (relevant) schema of your json already. You don't need dynamic in the first place, and even in .net 4.0 and higher I'd advise against using it. Code using dynamic is typically slower, more error prone, and harder to debug than code which is statically typed not just because of compile-time checks, but also because errors at runtime appear earlier.
From your limited example and without knowing what that First thing is, it looks to me like you could do something like...
class LocationFromJson {
public LocationContentsFromJson First;
}
class LocationContentsFromJson {
public string cafeID, cafeName, cafeState, cafeCity, cafeStreetName, cafeZip;
}
//Later usage; this should be equivalent to your example:
var deserialisedJson = JsonConvert.DeserializeObject<LocationFromJson[][][][]>(json);
var locations =
deserialisedJson //4 levels of enumerable
.SelectMany(o => o) //3 levels of enumerable
.SelectMany(o => o) //2 levels of enumerable
.SelectMany(o => o) //1 level of enumerable
.Select(o => new Location {
CafeId = o.First.cafeID,
CafeName = o.First.cafeName,
CafeState = o.First.cafeState,
CafeCity = o.First.cafeCity,
CafeStreetName = o.First.cafeStreetName,
CafeZip = o.First.cafeZip,
}).ToArray();
To be explicit: this may or may not work unaltered. Your example does not include the type declaration of Location nor an example json, so I'm speculating here a little: Location.CafeId might also be an int; I can't tell from your question.
But this shouldn't be too far from what you need.
Related
This is my problem: I have a nested dictionary (primary keys: orderIds, secondary keys: productIds).
My scheme:
Dictionary<string, Dictionary<string, List<Task>>>
I need to look for the productId and return that value (Task object).
It's usually not the case that a product key appears in several orders. So it's unique.
Here is a json example:
{
"O1": {
"P1": [
{
"Field": "V1"
},
{
"Field": "V7"
}
],
"P2": [
{
"Field": "V2"
},
{
"Field": "V8"
}
]
},
"O2": {
"P1": [
{
"Field": "V3"
},
{
"Field": "V5"
}
],
"P2": [
{
"Field": "V4"
},
{
"Field": "V6"
}
]
}
}
If I look for productId "P5" I want to get...
[{"Field":"V5"}]
The only way I found that runs is...
return base.Values.Single(x => x.ContainsKey(productId))[productId];
//base is the nested Dictionary
But I don't like what I'm doing here. Because I detach the correct dictionary from the collection (values) where the key exists and finally I fetch only the value via key filtering (collection[key]).
That are basically two steps, but I suspect there is an easier way with just one step. - I just can't find this one.
Maybe you can help me out. :)
For performance tuning, use HashSet instead of List.
I also can recommend ISBN: 0321637003 (LINQ to Objects), maybe there is an updated version. But anyway its still very good content.
If you have millions of entries in your dict, you can try PLINQ.
return base.Values.Single(x => x.ContainsKey(productId))[productId];
The Sample json throws an exception, because your productId (P1, P2) is not unique and P5 does not exist.
If I look for productId "P5" I want to get...
[{"Field":"V5"}]
There Is no P5
I am not sure its helping but, here is my sample code. I have put your json into a file and deserialize it.
var lFile = new FileInfo(#"C:\_test\data.json");
using var lReader = lFile.OpenText();
var lJsonStr = lReader.ReadToEnd();
var lDicDic = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, List<ProductId>>>>(lJsonStr);
//var lTest1 = lDicDic.Values.Single(x => x.ContainsKey("P1"))["P1"]; //Not working, P1 is not unique!
var lTest2 = lDicDic["O2"]["P1"];
//contains => "Field": "V3" + "Field": "V5"
var lTest3 = lDicDic
.SelectMany(p => p.Value.Values.SelectMany(qItem => qItem))
.FirstOrDefault(qProducts => qProducts.Field == "V5");
//contains => "Field": "V5"
var lTest4 = lDicDic["O2"]["P1"].Last();
//contains => "Field": "V5"
EDIT:
After your Fiddler code I was able to test it.
public List<MyTask> GetByProductId(string productId)
{
var lProductDict = Tasks.Values.SingleOrDefault(x => x.ContainsKey(productId));
return lProductDict?.GetValueOrDefault(productId);
}
My stomach tells my, your current approach is hard to make any better ;)
If you have a lot of calls on this nasted dict, and its changing not very often it could make sense to refactor the dict from TopDown to BottomUp at the beginning for the processing.
Anyway, If my comments are helpful plz upvote my answere :)
If I were you I would make some testcases with real data.
Dont forget: If you are in DEBUG mode, to enable "Optimize Code" at the Project. In my testcase, I had an AVG of 47ms without and 42ms with optimized code:
You can give PLINQ a try:
https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq
Here us my test scenario:
var lStopWatch = new Stopwatch();
lStopWatch.Restart();
var lJobs = new Jobs("F1");
const int TestOrderCount = 1000000;
const int TestAvgCount = 1000;
for (var lIndex = 1; lIndex < TestOrderCount; lIndex++)
{
var lNoStr = lIndex.ToString("D6");
lJobs.Add($"O{lNoStr}", $"P{lNoStr}", $"V{lNoStr}");
}
var GetTimes = new List<long>();
var lRandom = new Random();
var lTestCases = Enumerable
.Range(1, TestAvgCount - 1)
.Select(r => $"P{lRandom.Next(1, TestOrderCount - 1):D6}")
.ToList();
var lSetupTimeMs = lStopWatch.ElapsedMilliseconds;
Debug.WriteLine($"SetupTimeMs: {lSetupTimeMs}");
foreach (var lTestCase in lTestCases)
{
lStopWatch.Restart();
var lTest = lJobs.GetByProductId(lTestCase);
GetTimes.Add(lStopWatch.ElapsedMilliseconds);
}
var lAvg = GetTimes.Sum() / TestAvgCount; //AVG Ms per get
Debug.WriteLine($"AVG: {lAvg}");
I would like to merge these two anonymous objects:
var man1 = new {
name = new {
first = "viet"
},
age = 20
};
var man2 = new {
name = new {
last = "vo"
},
address = "123 street"
};
Into a single one:
var man = new {
name = new {
first = "viet",
last = "vo"
},
age = 20,
address = "123 street"
};
I looked for a solution but found nothing clever.
Convert the anonymous object to ExpandoObject which is essentially a dictionary of string key and object value:
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
public static ExpandoObject ToDynamic(this object obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var propertyInfo in obj.GetType().GetProperties())
{
var currentValue = propertyInfo.GetValue(obj);
if (propertyInfo.PropertyType.IsAnonymous())
{
expando.Add(propertyInfo.Name, currentValue.ToDynamic());
}
else
{
expando.Add(propertyInfo.Name, currentValue);
}
}
return expando as ExpandoObject;
}
I'm using a helper extension to establish whether a type is an anonymous one:
public static bool IsAnonymous(this Type type)
{
return type.DeclaringType is null
&& type.IsGenericType
&& type.IsSealed
&& type.IsClass
&& type.Name.Contains("Anonymous");
}
Then, merge two resulting expando objects into one, but recursively, checking for nested expando objects:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
public static IDictionary<string, object> MergeDictionaries(
IDictionary<string, object> targetDictionary,
IDictionary<string, object> sourceDictionary,
bool overwriteTarget)
{
foreach (var pair in sourceDictionary)
{
if (!targetDictionary.ContainsKey(pair.Key))
{
targetDictionary.Add(pair.Key, sourceDictionary[pair.Key]);
}
else
{
if (targetDictionary[pair.Key] is IDictionary<string, object> innerTargetDictionary)
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
targetDictionary[pair.Key] = MergeDictionaries(
innerTargetDictionary,
innerSourceDictionary,
overwriteTarget);
}
else
{
// What to do when target propety is nested, but source is not?
// Who takes precedence? Target nested property or source value?
if (overwriteTarget)
{
// Replace target dictionary with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
else
{
if (pair.Value is IDictionary<string, object> innerSourceDictionary)
{
// What to do when target propety is not nested, but source is?
// Who takes precedence? Target value or source nested value?
if (overwriteTarget)
{
// Replace target value with source dictionary.
targetDictionary[pair.Key] = innerSourceDictionary;
}
}
else
{
// Both target and source are not nested.
// Who takes precedence? Target value or source value?
if (overwriteTarget)
{
// Replace target value with source value.
targetDictionary[pair.Key] = pair.Value;
}
}
}
}
}
return targetDictionary;
}
The overwriteTarget parameter decides which object takes priority when merging.
Usage code:
var man1 = new
{
name = new
{
first = "viet",
},
age = 20,
};
var man2 = new
{
name = new
{
last = "vo",
},
address = "123 street",
};
var man1Expando = man1.ToDynamic();
var man2Expando = man2.ToDynamic();
dynamic result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
and the result:
{
"name": {
"first": "viet",
"last": "vo"
},
"age": 20,
"address": "123 street"
}
Notice how I assigned the result to dynamic. Leaving compiler assign the type will leave you with expando object presented as IDictionary<string, object>. With a dictionary representation, you cannot access properties in the same manner as if it was an anonymous object:
var result = MergeDictionaries(man1Expando, man2Expando, overwriteTarget: true);
result.name; // ERROR
That's why the dynamic. With dynamic you are losing compile time checking, but have two anonymous objects merged into one. You have to judge for yourself if it suits you.
There's nothing built-in in the C# language to support your use case. Thus, the question in your title needs to be answered with "Sorry, there is no easy way".
I can offer the following alternatives:
Do it manually:
var man = new {
name = new {
first = man1.name.first,
last = man2.name.first
},
age = man1.age,
address = man2.address
};
Use a class instead of an anonymous type for the resulting type (let's call it CompleteMan). Then, you can
create a new instance var man = new CompleteMan(); ,
use reflection to collect the properties and values from your "partial men" (man1 and man2),
assign those values to the properties of your man.
It's "easy" in the sense that the implementation will be fairly straight-forward, but it will still be a lot of code, and you need to take your nested types (name) into account.
If you desperately want to avoid non-anonymous types, you could probably use an empty anonymous target object, but creating this object (var man = new { name = new { first = (string)null, last = (string)null, ...) is not really less work than creating a class in the first place.
Use a dedicated dynamic data structure instead of anonymous C# classes:
The Newtonsoft JSON library supports merging of JSON objects.
Dictionaries can also be merged easily.
ExpandoObjects can be merged easily as well.
I have a dynamic object which basically holds an AvroRecord. AvroRecord class details here.
I can assign values to the properties statically but I was wondering if this could be done dynamically. I have looked at the forum question here ,here and also here. But none of these work for me.
This is the static code that works.
var serializer = AvroSerializer.CreateGeneric(Schema);
var rootSchema = serializer.WriterSchema as RecordSchema;
dynamic counterpartRow = new AvroRecord(rootSchema);
counterpartRow.CounterpartID = Row.CounterpartID
counterpartRow.CounterpartFirstDepositDate = Row.CounterpartFirstDepositDate
The Row is an object of the InputBuffer class of SSIS and it holds all the columns coming from the upstream data source.
The schema variable used above is an avro schema, which is something like this.
Schema = #"{
""type"":""record"",
""name"":""Microsoft.Hadoop.Avro.Specifications.Counterparts"",
""fields"":
[
{ ""name"":""CounterpartID"", ""type"":""int"" },
{ ""name"":""CounterpartFirstDepositDate"", ""type"":[""string"",""null""] },
{ ""name"":""CounterpartFirstTradeDate"",""type"":[""string"",""null""] },
{ ""name"":""ClientSegmentReportingID"",""type"":""int"" },
{ ""name"":""ClientSegmentReportingName"", ""type"":[""string"",""null""] },
{ ""name"":""ContractID"", ""type"":""int""},
{ ""name"":""ContractFirstDepositDate"", ""type"":[""string"",""null""]},
{ ""name"":""ContractFirstTradeDate"",""type"":[""string"",""null""] },
{ ""name"":""ContractClosingOffice"",""type"":[""string"",""null""] },
{ ""name"":""LeadCreationDate"", ""type"":[""string"",""null""] },
{ ""name"":""ContractCountryOfResidence"", ""type"":[""string"",""null""]}
]
}";
I have tried something like the earlier forum links suggested like
counterpartRow.GetType().GetField("CounterpartID").SetValue(Row, Row.CounterpartID, null);
and also the other method (which apparently should work for for dynamic type), but even that does not.
foreach (string propertyName in GetPropertyKeysForDynamic(counterpartRow.Schema.Fields()))
{
string propertyValue = counterpartRow[propertyName];
}
and the function defined like this.
public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
var jObject = (JObject)JToken.FromObject(dynamicToGetPropertiesFor);
Dictionary<string, object> values = jObject.ToObject<Dictionary<string, object>>();
List<string> toReturn = new List<string>();
foreach (string key in values.Keys)
{
toReturn.Add(key);
}
return toReturn;
}
The dictionary above returns blank.
the Row mentioned above is an object of InputBuffer class (auto generated class in SSIS).
which is something like this.
public class Input0Buffer: ScriptBuffer
{
public Input0Buffer(PipelineBuffer Buffer, int[] BufferColumnIndexes, OutputNameMap OutputMap)
: base(Buffer, BufferColumnIndexes, OutputMap)
{
}
public Int32 CounterpartID
{
get
{
return Buffer.GetInt32(BufferColumnIndexes[0]);
}
}
------more properties
If you see my original static code, I am trying to dynamically generate the assignment. I have already dynamically generated the schema (instead of the static definition I have given above). So, the only piece left to generate dynamically is the assignment of the assignment. An idea could be that I generate the string but how do I then execute that string? That is only if there is no way to achieve this.
If I understand correct your question I think you can try something like this
string _schema = "your avro schema"
RecordSchema _record = (RecordSchema)Avro.Schema.Parse(_schema);
GenericRecord _generic_record = new GenericRecord(payload_record);
for (int ii = 0; ii < _record.Fields.Count; ii++)
{
_generic_record.Add(_record.Fields[ii].Name, Raw.TheFiledYouNeed);
}
I'm trying to create some dynamic ExpandoObject. I've encountered a certain problem.
As I don't know what the name of these different properties in my objects should be, I can't do like this:
var list = new ArrayList();
var obj = new ExpandoObject();
obj.ID = 1,
obj.Product = "Pie",
obj.Days = 1,
obj.QTY = 65
list.Add(obj);
Let me explain my situation: I wish to get data from a random DB (I don't know which, but building a connection string from the information I get from the UI), therefore I don't know what data I need to get. This could be an example of a DB table
TABLE Sale
ID: int,
Product: nvarchar(100),
Days: int,
QTY: bigint
This could be another exmaple:
TABLE Foobar
Id: int,
Days: int
QTY: bigint
Product_Id: int
Department_Id: int
As you see, I don't know what the DB looks like (this is 100% anonymous, therefore it needs to be 100% dynamic), and the data I want to return should look like a well constructed JSON, like so:
[
{
"ID": 1,
"Product": "Pie"
"Days": 1,
"QTY": 65
},
{
"ID": 2,
"Product": "Melons"
"Days": 5,
"QTY": 12
}
]
Or, with the other example:
[
{
"ID": 1,
"Days": 2,
"QTY": 56,
"Product_Id": 5,
"Department_Id": 2
}
{
"ID": 2,
"Days": 6,
"QTY": 12,
"Product_Id": 2,
"Department_Id": 5
}
]
I've tried working with these ExpandoObjects, but can't seem to make it work, as I can't do what's illustrated in the top of this question (I don't know the names of the properties). Is there a way for me to say something like:
var obj = new ExpandoObject();
var propName = "Product";
var obj.propName = "Pie"
Console.WriteLine("Let's print!: " + obj.Product);
//OUTPUT
Let's print!: Pie
Does anyone have a solution, og simply guidance to a structure, that might solve this situation?
Rather than creating an ExpandoObject or some other dynamic type, you could create a List<Dictionary<string, object>> where each Dictionary<string, object> contains the name/value pairs you want to serialize. Then serialize to JSON using Json.NET (or JavaScriptSerializer, though that is less flexible):
var list = new List<Dictionary<string, object>>();
// Build a dictionary entry using a dictionary initializer: https://msdn.microsoft.com/en-us/library/bb531208.aspx
list.Add(new Dictionary<string, object> { { "ID", 1 }, {"Product", "Pie"}, {"Days", 1}, {"QTY", 65} });
// Build a dictionary entry incrementally
// See https://msdn.microsoft.com/en-us/library/xfhwa508%28v=vs.110%29.aspx
var dict = new Dictionary<string, object>();
dict["ID"] = 2;
dict["Product"] = "Melons";
dict["Days"] = 5;
dict["QTY"] = 12;
list.Add(dict);
Console.WriteLine(JsonConvert.SerializeObject(list, Formatting.Indented));
Console.WriteLine(new JavaScriptSerializer().Serialize(list));
The first outputs:
[
{
"ID": 1,
"Product": "Pie",
"Days": 1,
"QTY": 65
},
{
"ID": 2,
"Product": "Melons",
"Days": 5,
"QTY": 12
}
]
The second outputs the same without the indentation:
[{"ID":1,"Product":"Pie","Days":1,"QTY":65},{"ID":2,"Product":"Melons","Days":5,"QTY":12}]
Use dynamic, then cast to IDictionary<string, object> to loop through your properties:
dynamic obj = new ExpandoObject();
obj.Product = "Pie";
obj.Quantity = 2;
// Loop through all added properties
foreach(var prop in (IDictionary<string, object>)obj)
{
Console.WriteLine(prop.Key + " : " + prop.Value);
}
I've made a fiddle: https://dotnetfiddle.net/yFLy2u
Now this is a solution to your question... other answers like #dbc's might be better suited to the problem (which is not the question, really)
As you can see here ExpandoObject Class, the ExpandoObject is implementing IDictionary<string, object>, so you can use that fact like
IDictionary<string, object> obj = new ExpandoObject();
var propName = "Product";
obj[propName] = "Pie"
Console.WriteLine("Let's print!: " + obj[propName]);
// Verify it's working
Console.WriteLine("Let's print again!: " + ((dynamic)obj).Product);
While I was writing the answer, I see you already got proper answer. You can use a Dictionary<string, onject> or even Tuple.
But as per your original question, you wanted to add properties dynamically. For that you can refer to other answer using ExpandoObject. This is just the same solution (using ExpandoObject to dynamically add properties) with classes similar to your code.
//example classes
public class DictKey
{
public string DisplayName { get; set; }
public DictKey(string name) { DisplayName = name; }
}
public class DictValue
{
public int ColumnIndex { get; set; }
public DictValue(int idx) { ColumnIndex = idx; }
}
//utility method
public static IDictionary<string, object> GetExpando(KeyValuePair<DictKey, List<DictValue>> dictPair)
{
IDictionary<string, object> dynamicObject = new ExpandoObject();
dynamicObject["Station"] = dictPair.Key.DisplayName;
foreach (var item in dictPair.Value)
{
dynamicObject["Month" + (item.ColumnIndex + 1)] = item;
}
return dynamicObject;
}
Ans usage example:
var dictionaryByMonth = new Dictionary<DictKey, List<DictValue>>();
dictionaryByMonth.Add(new DictKey("Set1"), new List<DictValue> { new DictValue(0), new DictValue(2), new DictValue(4), new DictValue(6), new DictValue(8) });
dictionaryByMonth.Add(new DictKey("Set2"), new List<DictValue> { new DictValue(1), new DictValue(2), new DictValue(5), new DictValue(6), new DictValue(11) });
var rowsByMonth = dictionaryByMonth.Select(item => GetExpando(item));
First part, read this blog post by C# team thoroughly.
Lets see your code
var obj = new ExpandoObject();
var propName = "Product";
var obj.propName = "Pie"
Console.WriteLine("Let's print!: " + obj.Product);
//OUTPUT
Let's print!: Pie
In your code you are using var obj = new ExpandoObject();, so you are creating a statically typed object of type ExpandableObject. In the blog they specifically call out
I didn’t write ExpandoObject contact = new ExpandoObject(), because if I did contact would be a statically-typed object of the ExpandoObject type. And of course, statically-typed variables cannot add members at run time. So I used the new dynamic keyword instead of a type declaration, and since ExpandoObject supports dynamic operations, the code works
So if you rewrite your code to use dynamic obj, and add the dynamic properties as properties it should work!
But for your particular use case you better use Dictionaries as suggested above by #dbc
dynamic obj = new ExpandoObject();
obj.Product= "Pie"
Console.WriteLine("Let's print!: " + obj.Product);
//OUTPUT
Let's print!: Pie
For those of you familiar with Minecraft, the 1.8 update stores the sounds as a file with an encrypted hash as the name (which you can really just change the extension to .ogg to play). There is an index stored as a JSON file in the assets folder which shows the proper sound name for each file with the encrypted hash name.
I'm trying to create a program that which the user types the name and it will find the sound(s) that contains that name. The index is stored in this fashion:
{ "objects":{"minecraft/sounds/mob/wither/idle2.ogg": {
"hash": "6b2f86a35a3cd88320b55c029d77659915f83239",
"size": 19332
},
"minecraft/lang/fil_PH.lang": {
"hash": "e2c8f26c91005a795c08344d601b10c84936e89d",
"size": 74035
},
"minecraft/sounds/note/snare.ogg": {
"hash": "6967f0af60f480e81d32f1f8e5f88ccafec3a40c",
"size": 3969
},
"minecraft/sounds/mob/villager/idle1.ogg": {
"hash": "a772db3c8ac37dfeb3a761854fb96297257930ab",
"size": 8605
},
"minecraft/sounds/mob/wither/hurt3.ogg": {
"hash": "a4cf4ebe4c475cd6a4852d6b4228a4b64cf5cb00",
"size": 16731
}
For example if the user types wither, it will grab the hashes for "minecraft/sounds/mob/wither/idle2.ogg"
and
"minecraft/sounds/mob/wither/hurt3.ogg"
My question is, how do I get the object names (the names, not the properties) to compare with the user's keyword string.
Sorry if I didn't use proper terminology for some words, I don't tinker with JSON files much. Correct my terminology as needed.
EDIT
This answer solves it a lot more nicely (without dynamic):
https://stackoverflow.com/a/32129497/563532
Original answer:
This works:
var obj = JsonConvert.DeserializeObject<dynamic>(#"{ ""objects"":{""minecraft/sounds/mob/wither/idle2.ogg"": {
""hash"": ""6b2f86a35a3cd88320b55c029d77659915f83239"",
""size"": 19332
},
""minecraft/lang/fil_PH.lang"": {
""hash"": ""e2c8f26c91005a795c08344d601b10c84936e89d"",
""size"": 74035
},
""minecraft/sounds/note/snare.ogg"": {
""hash"": ""6967f0af60f480e81d32f1f8e5f88ccafec3a40c"",
""size"": 3969
},
""minecraft/sounds/mob/villager/idle1.ogg"": {
""hash"": ""a772db3c8ac37dfeb3a761854fb96297257930ab"",
""size"": 8605
},
""minecraft/sounds/mob/wither/hurt3.ogg"": {
""hash"": ""a4cf4ebe4c475cd6a4852d6b4228a4b64cf5cb00"",
""size"": 16731
}
}
}");
var t = obj.objects;
var names = new HashSet<String>();
foreach(JProperty fileThing in t)
{
names.Add(fileThing.Name);
}
names.Dump();
Gives:
minecraft/sounds/mob/wither/idle2.ogg
minecraft/lang/fil_PH.lang
minecraft/sounds/note/snare.ogg
minecraft/sounds/mob/villager/idle1.ogg
minecraft/sounds/mob/wither/hurt3.ogg
You can also do this:
var t = obj.objects;
var names = new Dictionary<String, String>();
foreach(JProperty fileThing in t)
{
names.Add(fileThing.Name, (string)t[fileThing.Name].hash);
}
Which gives you a dictionary linking the original name to the hash:
minecraft/sounds/mob/wither/idle2.ogg -> 6b2f86a35a3cd88320b55c029d77659915f83239
minecraft/lang/fil_PH.lang -> e2c8f26c91005a795c08344d601b10c84936e89d
minecraft/sounds/note/snare.ogg -> 6967f0af60f480e81d32f1f8e5f88ccafec3a40c
minecraft/sounds/mob/villager/idle1.ogg -> a772db3c8ac37dfeb3a761854fb96297257930ab
minecraft/sounds/mob/wither/hurt3.ogg -> a4cf4ebe4c475cd6a4852d6b4228a4b64cf5cb00
Assuming you have a jsonString as a string variable.
jsonString = "";
JArray array = JArray.Parse(json);
foreach (JObject content in array.Children<JObject>())
{
foreach (JProperty prop in content.Properties())
{
Console.WriteLine(prop.Name);
}
}