I have the following text that I need to put into a dictionary. At first sight I thought that it would be very easy but at the end I found myself doing extensive string search and finding sometimes values that break the parser.
"0":
{
"key":"valueWithAnyCharInside",
"key2":"valueWithAnyCharInside",
"key3":"valueWithAnyCharInside"
},
This will map into the following model:
class Item
{
private int id;
private Dictionary<string, string> data;
}
Any ideas? Maybe using regex ...
Your data format is probably a JSON, but you gave only a part of it. I've modified it slightly as:
{"0":
{
"key":"valueWithAnyCharInside",
"key2":"valueWithAnyCharInside",
"key3":"valueWithAnyCharInside"
}
}
now you can parse it as following:
string json = ...; //your json goes here
var serializer = new JavaScriptSerializer();
var parsed = serializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(json);
//printing data
parsed["0"].Select(pair => string.Format( "{0} - {1}", pair.Key, pair.Value))
.ToList()
.ForEach(Console.WriteLine);
prints:
key - valueWithAnyCharInside
key2 - valueWithAnyCharInside
key3 - valueWithAnyCharInside
To get strongly typed List<Item> use next code
List<Item> items = parsed.Select(pair => new Item { Id = int.Parse(pair.Key),
Data = pair.Value})
.ToList();
Where Item is :
class Item
{
public int Id { get; set; }
public Dictionary<string, string> Data {get;set;}
}
Related
I want to take some Json and parse it in to a collection of key/value pairs, but some of the values will be dictionaries themselves. I tried the usual Newtonsoft deserialization. It's close, but not quite right. The end result must be a dictionary, not a strongly typed class.
This is some example Json:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job"
}
]
}
This is the code I used to deserialize:
var json = File.ReadAllText(#"c:\temp\job.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
The result is almost correct, but the value of the item with a key of "JobNotes" is just json string. I want the parser to recurse in and deserialise the inner Json to a further dictionary of strings and objects. Is there a way I can do this with the Newtonsoft library? Or, is there another library that will do the trick? Can I hook in to the parsing method to override the functionality at that point in time?
This is a modified version of #DanielKeogh's code. It works well.
class Program
{
static void Main(string[] args)
{
var json = File.ReadAllText(#"c:\temp\job3.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
RecurseDeserialize(result);
}
private static void RecurseDeserialize(Dictionary<string, object> result)
{
//Iterate throgh key/value pairs
foreach (var keyValuePair in result.ToArray())
{
//Check to see if Newtonsoft thinks this is a JArray
var jarray = keyValuePair.Value as JArray;
if (jarray != null)
{
//We have a JArray
//Convert JArray back to json and deserialize to a list of dictionaries
var dictionaries = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jarray.ToString());
//Set the result as the dictionary
result[keyValuePair.Key] = dictionaries;
//Iterate throught the dictionaries
foreach (var dictionary in dictionaries)
{
//Recurse
RecurseDeserialize(dictionary);
}
}
}
}
}
This modified Json shows how deep it goes:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job",
"JobNoteNotes": [
{
"Something": 1,
"Something2": "Test"
}
]
}
]
}
The result ends three dictionaries deep so that I can get at the "Something" value by key.
This can be done with a little recursion. I'll leave defining IsJson up to you as an academic exercise. :)
Dictionary<string, object> RecursiveDeserialize(string json)
{
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var pair in result.ToArray())
{
if(IsJson(pair.Value))
{
result[pair.Key] = RecursiveDeserialize(pair.Value);
}
}
return result;
}
Using this object for json string
public class JobNote
{
public string NoteText { get; set; }
public string NoteType { get; set; }
}
public class ListJob
{
public int JobNumber { get; set; }
public object Asset { get; set; }
public List<JobNote> JobNotes { get; set; }
}
Then you can deserialize it
I am trying to convert a list to a json string. The json is generating fine, but when I try to generate an array within the JSON it won't format properly.
My list is generated
List<string> invoicesList = new List<string>();
foreach (var invoice in theInvoices)
{
invoicesList.Add(invoice.InvoiceNumber);
invoicesList.Add(String.Format("{0:c}", invoice.GrandTotal));
invoicesList.Add(FieldTranslation.ToShortDate(invoice.Date.ToString()));
}
Then I add it to the JSON
var info = new MobileAPIHelper.ClientPayrollInfo
{
GrossWages = String.Format("{0:c}", GrossWages),
InvoiceTotal = String.Format("{0:c}", invoiceTotal),
InvoiceDate = FieldTranslation.ToShortDate(invoiceDate.ToString()),
InvoiceList = invoicesList.ToArray()
};
The output ends up just being a long JSON string with everything from the list
"InvoiceList":["SALES000000000006","$9,300.00","4/11/2016","SALES000000000008","$4,650.00","12/22/2015"]
What I can't figure out is how to get the list / json to format that invoicelist like so:
"InvoiceList":[{
"SALES000000000006","$9,300.00","4/11/2016"
},{
"SALES000000000008","$4,650.00","12/22/2015"
}]
invoicesList is not a list of an object that contains those values, it's a list of strings. You need to make a class that acts as a container for
invoice.InvoiceNumber;
String.Format("{0:c}", invoice.GrandTotal);
invoice.Date.ToString());
these fields. Make invoicesList a list of that class, then parse it to json. You're adding raw strings.
If you make those string as object.
public class Invoice{
public string InvoiceNumber{get;set;}
public string GrandTotal{get;set;}
public string Date{get;set;}
}
List<Invoice> invoicesList = new List<Invoice>();
foreach (var invoice in theInvoices)
{
invoicesList.Add(new Invoice(){InvoiceNumber=invoice.InvoiceNumber,
GrandTotal= invoice.GrandTotal,
Date=FieldTranslation.ToShortDate(invoice.Date.ToString())});
}
Then there is a package called NewtonSoftJson which allows you to convert from Collection.
Go to package manager console (Click on view menu in visual studio-> Other windows -> Package Manager console)
Install-Package Newtonsoft.Json
C# code
var invoiceListString=JsonConvert.SerializeObject(invoicesList);
I will recommend to use NewtonSoft.Json.
public class Invoice
{
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public string InvoiceTotal { get; set; }
}
public void PrintJson()
{
List<Invoice> InvoiceList = new List<Invoice>();
var outputObject = new { InvoiceList };
InvoiceList.Add(new Invoice { InvoiceNumber = "SALES0000001", InvoiceDate = DateTime.UtcNow, InvoiceTotal = String.Format("{0:c}", "90000") });
InvoiceList.Add(new Invoice { InvoiceNumber = "SALES0000002", InvoiceDate = DateTime.UtcNow, InvoiceTotal = String.Format("{0:c}", "60000") });
var output1 = JsonConvert.SerializeObject(outputObject);
var output2 = JsonConvert.SerializeObject(InvoiceList);
}
Output1
{"InvoiceList":[{"InvoiceNumber":"SALES0000001","InvoiceDate":"2017-02-22T16:46:31.6933956Z","InvoiceTotal":"90000"},{"InvoiceNumber":"SALES0000002","InvoiceDate":"2017-02-22T16:46:31.6933956Z","InvoiceTotal":"60000"}]}
Output2
[{"InvoiceNumber":"SALES0000001","InvoiceDate":"2017-02-22T16:46:31.6933956Z","InvoiceTotal":"90000"},{"InvoiceNumber":"SALES0000002","InvoiceDate":"2017-02-22T16:46:31.6933956Z","InvoiceTotal":"60000"}]
Please notice the Json in your question is not valid.
As pointed by Dispersia, your list is one big string list and, therefore, the json serializer behaves as requested -- turns this big string list into a big json string array.
I haven't got VS at hand to test the code below but try turning your invoice list into something like:
List<Tuple<String, String, String>> invoiceList = new List<>();
and then add tuples accordingly:
foreach (var invoice in theInvoices)
{
Tuple<String, String, String> t = new Tuple<>(
invoice.InvoiceNumber,
String.Format("{0:c}", invoice.GrandTotal),
FieldTranslation.ToShortDate(invoice.Date.ToString()));
invoicesList.Add(t);
}
Would that help?
I am trying to send a key-pair based array object using ajax, here array is created dynamically
["{\"key\":\"#c1\",\"value\":\"g1\"}","{\"key\":\"#c1\",\"value\":\"g2\"}", "{\"key\":\"#c2\",\"value\":\"g3\"}", "{\"key\":\"#c4\",\"value\":\"g4\"}"]
Above is json formatted data which i am sending to a method and able to receive it. At c# end Dictionary<string, string> Columns is used. problem here is that the key values are just number and values contain each element of above mentioned json data as shown below,
foreach(var eachVals in columns)
{
string k = eachVals.Key;
string col = eachVals.Value;
}
when iterating the dictionary eachVals.key is array index (0,1,...) and eachVals.Value contains {"key":"#c1","value":"g1"}
So what i want is separate keys i.e "#c1","#c1","#c2"... and values i.e "g1","g2"...
You would need to deserialize the values from col. Using Newtonsoft.Json, would look something like this:
Dictionary<string, string> columns = new Dictionary<string, string>
{
{ #"0", #"{""key"": ""#c1"", ""value"":""g1"" }" },
{ #"1", #"{""key"": ""#c2"", ""value"":""g2"" }" }
};
var result = columns.ToDictionary(
column => JsonConvert.DeserializeObject<MyObj>(column.Value).key,
column => JsonConvert.DeserializeObject<MyObj>(column.Value).value);
Where MyObj is:
internal class MyObj
{
public string key { get; set; }
public string value { get; set; }
}
I think you can do or get the idea from this:
foreach(var eachVals in columns)
{
var e = eachVals.Value.Split(',');
e = e[0].Split(':');
string k = e[0];
string v = e[1];
}
I try to put my XML File into a Dictionary with Generic List. How I can merge correct the query List to my dictionary with correct key? Instead of .ToList() .ToDictionary is not possible?
<?xml version="1.0"?>
<customers>
<cust ID="1" DeviceID="1" Name="Bob" Latitude="10" Longitude="58" Device="HW1.0"> </cust>
<cust ID="2" DeviceID="2" Name="Jack" Latitude="28" Longitude="20" Device="HW2.2"> </cust>
</customers>
//XML attribute Name is Dict Key
public class Customers
{
public int Longitude { get; set; }
public int Latitude { get; set; }
public int DeviceID { get; set; }
public string Device { get; set; }
}
class Program
{
private static Dictionary<string, List<Customers>> ReadXmlToDict(string filename)
{
// Should be Key = Xml Attribute Name Value, List of class
Dictionary<string, List<Customers>> dict = new Dictionary<string, List<Customers>>();
XDocument xdoc = XDocument.Load(filename);
var querylist = (from row in xdoc.Descendants("cust")
select new Customers()
{
//Name = (string)row.Attribute("Name"), // Wrong here should be the Dic key
DeviceID = (int)row.Attribute("DeviceID"), // list value
Longitude = (int)row.Attribute("Longitude"), // list value
Latitude = (int)row.Attribute("Latitude"), // list value
Device = (string)row.Attribute("Device") // list value
}).ToList();
return null; // null for test To.List and not Dict
}
This is how I would implement it, I think it accomplishes what you're looking for. You have a class called Customers and then want to store a list of those customers with a single key...I don't follow that logic.
I created a class called Customer, which houses the information for a singular customer. Since you're returning a Dictionary<string, Customer>, where the string is the unique attribute Name in the xml, there is no use case for the value of your dictionary being a List<Customer>. Perhaps if you have multiple customers under the same name, you would use this, but why not then make the key the (I assume) truly unique identifier, the DeviceID?
namespace TestConsole
{
class Customer
{
public int DeviceID;
public int Longitude;
public int Latitude;
public string Device;
}
class Program
{
private static Dictionary<string, Customer> ReadXmlToDictionary(string filename)
{
var dict = new Dictionary<string, Customer>();
var doc = XDocument.Load(#"C:\test.xml");
dict = doc.Descendants("cust")
.ToDictionary(
row => (string)row.Attribute("Name"),
row => new Customer {
Device = (string)row.Attribute("Device"),
DeviceID = (int)row.Attribute("DeviceID"),
Latitude = (int)row.Attribute("Latitude"),
Longitude = (int)row.Attribute("Longitude")
});
return dict;
}
static void Main(string[] args)
{
ReadXmlToDictionary(null);
}
}
}
EDIT: Thought the performance related answer was interesting, so decided to try it out for this single level xml (using ID as the unique identifier). Here are the results:
1019 Descendants took 0.0030257 seconds.
1019 Elements took 0.0028348 seconds.
10000 Descendants took 0.0098942 seconds.
10000 Elements took 0.0101478 seconds.
100000 Descendants took 0.0873025 seconds.
100000 Elements took 0.1223577 seconds.
EDIT:
After creating your xsd, and generating a class from it, you would then use it as such:
var parsed = XDocument.Parse(doc.ToString());
var serializer = new XmlSerializer(typeof(Xsds.customers));
var typedPayload = serializer.Deserialize(doc.Root.CreateReader());
var xmlAsClass = (TestConsole.Xsds.customers)typedPayload;
dict = xmlAsClass.cust
.ToDictionary(
row => (int)row.ID,
row => new Customer {
Device = row.Device,
DeviceID = row.DeviceID,
Latitude = row.Latitude,
Longitude = row.Longitude,
Name = row.Name
});
You can do it easily by using ToDictionary() extension method. But performance wise, it is far better to use Elements() method rather than Descendants(); For further reading please read this blog post:
WHY (OR WHEN) YOU SHOULD/SHOULDN’T USE DESCENDANTS() METHOD
And your query will look like this:
var customersDictionary =
xDoc.Root
.Elements("cust")
.ToDictionary(xe =>
xe.Attribute("Name").Value, xe =>
new Customers
{
DeviceID = (int)xe.Attribute("DeviceID"),
Longitude = (int)xe.Attribute("Longitude"),
Latitude = (int)xe.Attribute("Latitude"),
Device = (string)xe.Attribute("Device")
});
I have an object that I obtained from an xml file and it contains a field of SeriesCode and ProductCodes associated with the specified Series code.
i.e.
SeriesCodeA
prodCode1
prodCode2
prodCode3
SeriesCodeA
prodCode4
prodCode5
prodCode6
I created a the following dictionary
Dictionary<string, List<string>> dictSeries = new Dictionary<string, List<string>>();
The values from the object I want to add to the dictionary. So in other words I want to, for each series code added to the Dictionary key as a string value add the corresponding product codes as string values.
something like
foreach (var s in series)
{
string code = s.SeriesCode;
if (dictSeries.ContainsKey(code))
{
foreach (var l in s.ProductCodes)
{
dictSeries[code].Add(l);
}
}
}
The above is not quite right
How can I add the SeriesCodes as keys to the dictionary with the corresponding product codes as string values(List)?
Use LINQ and ToDictionary method to make it more readable:
var dictSeries = series.ToDictionary(x => x.SeriesCode, x => x.ProductCodes);
A assumed your Series class looks like that:
class Serie
{
public string SeriesCode { get; set; }
public List<string> ProductCodes { get; set; }
}
Your code also will work, but needs few improvements:
foreach (var s in series)
{
string code = s.SeriesCode;
if (!dictSeries.ContainsKey(code))
{
dictSeries.Add(code, new List<string>());
}
foreach (var l in s.ProductCodes)
{
dictSeries[code].Add(l);
}
}