I'm trying to post two fields and a bundled object with two fields to Mailchimp's API endpoint.
var store_id = ConfigurationManager.AppSettings["MailChimpStoreID"];
var method = String.Format("ecommerce/stores/{0}/products?", store_id);
var id = "id123";
var title = "testproduct";
//var variants = new {id, title };
var productData = new { id, title, variants = new { id, title } };
var requestJson = JsonConvert.SerializeObject(productData);
When I POST my data and do a try-catch around my code to check, I see that my requestJson returns the following:
{
"id":"id123",
"title":"testproduct",
"variants":{"id":"id123","title":"testproduct"}
}
I know the issue is that the variants when serialized isn't returning as "variants":[{"foo":bar"}] but how do I resolve it so that my code bundles this as an object correctly?
Second theory: Since C# is a strongly-typed Object Oriented program, do I need to define the objects above with get:sets and then call them into my function?
you should write it like this,
var productData = new { id, title, variants = new[] {new { id, title }} };
Console.WriteLine(JsonConvert.SerializeObject(productData));
//Prints:
{"id":1,"title":"sodijf","variants":[{"id":1,"title":"sodijf"}]}
You can use either dynamic or an object as the type of list as well.
var productData = new { id, title, variants = new List<object>() {new { id, title }} };
Related
I have a problem; I have various (too many) classes, that are linked as Parent/Child
I populate initially my top class while instantiate the others, as follows (works):
TopClass MyClass = new TopClass()
{
Headers = new someHeaders()
{
CorrID = "1234567890",
RequestID = "1234567890",
L_Token = "abcdefghijklmnopqrstuvwxyz"
},
Body = new someBody()
{
access = new AccessHeaders()
{
allPs = "allAcc",
availableAcc = "all"
},
CombinedServiceIndicator = false,
FrequencyPerDay = 10,
ValidUntil = "2020-12-31"
},
ResponseAttributes = new ConsentResponse()
{
myId = String.Empty,
myStatus = String.Empty,
myName = String.Empty,
_links_self_href = String.Empty,
status_href = String.Empty
}
};
The initials values that I populate above rarely change, but the Classes' properties change as the project goes on, and each class ends up having many properties.
I need to parse the properties and set values to any properties that match their name, but I can't seem to figure out why despite following official examples.
(I read the input data from a JSON string I use Newtonsoft - have tried everything out there)
I can't find a way to deserialize the JSON and assign any values to my properties without using the name of the property, i.e. without saying
MyClass.Body.access.allPs = "Hello"
but something like
var json = response.Content.ReadAsStringAsync().Result.ToString();
var a = new { serverTime = "", data = new object[] { } };
var c = new JsonSerializer();
or
if (myresponse.Attributes.GetType().GetTypeInfo().GetDeclaredProperty(key) != null)
myresponse.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(myresponse, entry.Value);
//and how do I read the value, parse my TopClass and assign the value to correct property?
Could someone help me?
Values for the properties will get automatically assigned if the JSON is having the correct structure as per the class mentioned above.
Something like:
{
"Body":
"access:
{
"allPs" = "required value"
}
}
Then you can use:
var result = JsonConvert.DeserializeObject < TopClass > (json );
enter image description hereI have a keyValupair of Hotel details;:
//This is where the data comes from : -
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
dynamic hotels1 = (dynamic)json_serializer.DeserializeObject(jso);
var keyValuePairs = hotels1["hotels"];
var hotelList = keyValuePairs["hotels"]; // hotelList[0] ={'key'='Code' ;value='123'}
//{'key'='Name'
// ;value='Sheraton'}
how do i convert this to a list of Hotel
List<Hotel> hotaals = new List<Hotel>();
where Hotel is
public class Hotel
{
public int code { get; set; }
public string name { get; set; }
}
I use a for loop to map fields, but my great boss says its inefficient and i have to use Linq.
The loop i use
foreach (dynamic h in hotelList)
{
oneHotel = new Hotel();
oneHotel.code = h["code"];
oneHotel.name = h["name"];
myHotels.Add(oneHotel);
}
Well the brute force way would be to just project the dictionary to objects by hard-coding the properties:
List<Hotel> hotaals = hotelList.Select(kvp => new Hotel {
code = kvp['Code'],
name = kvp["Name"]
})
.ToList();
I would also challenge what your "great boss" means by inefficient".
So first you get your initial data:
var hotelList = keyValuePairs["hotels"];
Then use linq to create your new list:
var hotelObjects = hotelList.Select(hotel => new Hotel { code = hotel.key, name = hotel.name});
Now to be clear what linq is doing under the hood is an iterative loop through the objects (just like foreach) and creates a new Hotel object for each item in the hotelList and returns them as an IQueryable<Hotel>. Just apply .ToArray() or .ToList() if you don't want an IQueryable<>
Now from what it sounds like your initial List of hotel details isn't structured so you might have to modify my supplied linq query above to suit the structure of the list.
You may need something closer to this:
// Gives IQueryable<Hotel> as result
var hotelObjects = hotelList.Select(hotel => new Hotel{code = hotel["key"], name = hotel["name"]});
// Gives Array<Hotel> as result
var hotelObjects = hotelList.Select(hotel => new Hotel{code = hotel["key"], name = hotel["name"]}).ToArray();
// Gives List<Hotel> as result
var hotelObjects = hotelList.Select(hotel => new Hotel{code = hotel["key"], name = hotel["name"]}).ToList();
I am trying to get my head around a problem. I am building an application where we are indexing assets in Elastic. The nature of the assets is very dynamic, because they contain client metadata, which is different from client to client.
Because of this, the Index is built from a List of dynamics in C#. This actually works like a charm. The problem is, I cannot control the _id property in Elastic when using the C# interface. This means when I update the documents, instead of updating the correct one a new duplicate is made.
My code looks like this:
List<dynamic> assets = new List<dynamic>();
var settings1 = new ConnectionSettings(
new Uri("http://localhost:9200")
).DefaultIndex("assets");
var client = new ElasticClient(settings1);
//assets is build here
var indexResponse = client.Indices.Create("assets");
var BulkResponse = client.IndexMany(assets);
This actually works and the index is built as I expect it to - almost. Even though I have a property called Id on the dynamic, it is not inferred correctly, which means the document is given an _Id decided by Elastic. Thus the next time I run this code using the same Id a new document is created rather than updated.
I have been searching high and low, but cannot seem to find a good solution. One thing I have tried is the following:
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id(s.Id)));
But this throws an error I cannot catch in the .net kernel. This actually works on lower versions on Elastic, but seems to have been broken with 7.2 and 7.0.1 of the C# interface.
Any help is much appreciated.
To allow the following to work
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id(s.Id)));
You just need to cast the Id type to the type that it is. For example, if it's a string
var client = new ElasticClient();
var assets = new dynamic[]
{
new { Id = "1", Name = "foo" },
new { Id = "2", Name = "bar" },
new { Id = "3", Name = "baz" },
};
var bulkResponse = client.Bulk(bd => bd.IndexMany(assets, (descriptor, s) => descriptor.Id((string)s.Id)));
This is a runtime limitation.
Instead of using dynamic type you could create dictionary-based custom type like:
public class DynamicDocument : Dictionary<string, object>
{
public string Id => this["id"]?.ToString();
}
and use it as follow:
class Program
{
public class DynamicDocument : Dictionary<string, object>
{
public string Id => this["id"]?.ToString();
}
static async Task Main(string[] args)
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
connectionSettings.DefaultIndex("documents");
var client = new ElasticClient(connectionSettings);
await client.Indices.DeleteAsync("documents");
await client.Indices.CreateAsync("documents");
var response = await client.IndexAsync(
new DynamicDocument
{
{"id", "1"},
{"field1", "value"},
{"field2", 1}
}, descriptor => descriptor);
//will update document with id 1 as it's already exists
await client.IndexManyAsync(new[]
{
new DynamicDocument
{
{"id", "1"},
{"field1", "value2"},
{"field2", 2}
}
});
await client.Indices.RefreshAsync();
var found = await client.GetAsync<DynamicDocument>("1");
Console.WriteLine($"Id: {found.Source.Id}");
Console.WriteLine($"field1: {found.Source["field1"]}");
Console.WriteLine($"field2: {found.Source["field2"]}");
}
}
Output:
Id: 1
field1: value2
field2: 2
Tested with elasticsearch 7.2.0 and NEST 7.0.1.
Hope that helps.
When data from a device goes into the elastic there are duplicates. I like to avoid this duplicates. I'm using a object of IElasticClient, .NET and NEST to put data.
I searched for a method like ElasticClient.SetDocumentId(), but cant find.
_doc doc = (_doc)obj;
HashObject hashObject = new HashObject { DataRecordId = doc.DataRecordId, TimeStamp = doc.Timestamp };
// hashId should be the document ID.
int hashId = hashObject.GetHashCode();
ElasticClient.IndexDocumentAsync(doc);
I would like to update the data set inside the Elastic instead of adding one more same object right now.
Assuming the following set up
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool)
.DefaultIndex("example")
.DefaultTypeName("_doc");
var client = new ElasticClient(settings);
public class HashObject
{
public int DataRecordId { get; set; }
public DateTime TimeStamp { get; set; }
}
If you want to set the Id for a document explicitly on the request, you can do so with
Fluent syntax
var indexResponse = client.Index(new HashObject(), i => i.Id("your_id"));
Object initializer syntax
var indexRequest = new IndexRequest<HashObject>(new HashObject(), id: "your_id");
var indexResponse = client.Index(indexRequest);
both result in a request
PUT http://localhost:9200/example/_doc/your_id
{
"dataRecordId": 0,
"timeStamp": "0001-01-01T00:00:00"
}
As Rob pointed out in the question comments, NEST has a convention whereby it can infer the Id from the document itself, by looking for a property on the CLR POCO named Id. If it finds one, it will use that as the Id for the document. This does mean that an Id value ends up being stored in _source (and indexed, but you can disable this in the mappings), but it is useful because the Id value is automatically associated with the document and used when needed.
If HashObject is updated to have an Id value, now we can just do
Fluent syntax
var indexResponse = client.IndexDocument(new HashObject { Id = 1 });
Object initializer syntax
var indexRequest = new IndexRequest<HashObject>(new HashObject { Id = 1});
var indexResponse = client.Index(indexRequest);
which will send the request
PUT http://localhost:9200/example/_doc/1
{
"id": 1,
"dataRecordId": 0,
"timeStamp": "0001-01-01T00:00:00"
}
If your documents do not have an id field in the _source, you'll need to handle the _id values from the hits metadata from each hit yourself. For example
var searchResponse = client.Search<HashObject>(s => s
.MatchAll()
);
foreach (var hit in searchResponse.Hits)
{
var id = hit.Id;
var document = hit.Source;
// do something with them
}
Thank you very much Russ for this detailed and easy to understand description! :-)
The HashObject should be just a helper to get a unique ID from my real _doc object. Now I add a Id property to my _doc class and the rest I will show with my code below. I get now duplicates any more into the Elastic.
public void Create(object obj)
{
_doc doc = (_doc)obj;
string idAsString = doc.DataRecordId.ToString() + doc.Timestamp.ToString();
int hashId = idAsString.GetHashCode();
doc.Id = hashId;
ElasticClient.IndexDocumentAsync(doc);
}
I am trying to add an object to my Algolia database using a slightly different structure provided by the documentation so that I don't have to type out the Json object in string format, however I ran into an error stating
Cannot implicitly convert anonymous type to
System.Collections.Generic.List
I see the red error message for all of the key/values in the objs variable.
var songIndexHelper = HttpContext.Application.Get("SongIndexHelper") as IndexHelper<SongAlgoliaModel>;
List<JObject> objs = new List<JObject>();
objs = new
{
ApprovalFL = false,
FreeFL = album.FreeFL,
LicenseFL = album.LicenseFL,
AccountInfoID = album.AccountInfoID,
AlbumID = album.AlbumID,
SongID = song.SongID,
BPM = song.BPM,
AccountImageURL = album.AccountInfo.ImageURL,
AccountType = "Artist",
AlbumName = album.AlbumName,
Artist = artist,
FeaturedArtist = songArtistsList,
ImageURL = album.ImageURL,
iTunesURL = album.iTunesURL,
LabelName = album.LabelName,
Title = album.AlbumName,
UserID = album.AccountInfo.UserID,
UploadDate = song.UploadDate,
Duration = song.Duration,
objectID = song.SongID
};
songIndexHelper.AddObjects(objs);
Here's the reference to documentation: https://www.algolia.com/doc/api-reference/api-methods/add-objects/
Edit alternative method however, my formatting of LicenseFL is off
List<JObject> objs = new List<JObject>();
objs.Add(JObject.Parse(#"{""ApprovalFL"":false, ""FreeFL"":" + album.FreeFL + ",""LicenseFL"":" +album.LicenseFL+ "}"));
songIndexHelper.AddObjects(objs);
The Algolia docs are unfortunately focused on the use of JObject (and JSON strings) which makes it reasonably easy to make mistakes (e.g. invalid JSON).
This is an approach you might like to consider:
var anon = new
{
ApprovalFL = true,
// Any other properties here
objectID = song.SongID
};
var obj = JObject.FromObject(anon);
var objs = new List<JObject> { obj };
songIndexHelper.AddObjects(objs);
Now you get some level of safety due to the anon variable (e.g. don't have to worry about invalid JSON strings) but also easy interaction with the Algolia API (which is documented in terms of JObject).