I'm struggling to make sense of Firestore arrays in Unity/C#.
DocumentSnapshot snap = task.Result;
IDictionary<string, object> dict = snap.ToDictionary();
object questData = dict["questData"];
How do I say results = dict["questData"][0].results?
Debugger shows questData as object I can't figure out how to say:
object[] questData = dict["questData"]
It comes in from Firestore as an object but it's really an array.
i dont see if its array or list oe IEnumerable..
but you could use that to unbox object:
var x =dict["quesData"] as object[] or List<object>
and following the type you apply the same schema.
if collection (ict["questData"]) is dictionary you could apply that:
foreach (KeyValuePair<string, object> pair in dict["questData"])
{
Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
}
to find the other keys, i dont see if key is string, so object should does the job in case...
so you could write too: var x =dict["quesData"] as Dictionary<string, object>
Related
I try to use the Criteria API to create a dynamic JPA-Query. I need to find a key-value pair inside a map of the object.
The Object looks similar to the following one.
public class item {
private UUID id;
#Column(name = "properties", columnDefinition = "nvarchar")
private Map<String, Object> properties;
}
I thought I could use the MapJoin join or joinMap:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Item> criteriaQuery = cb.createQuery(Item.class);
Root<Item> itemRoot = criteriaQuery.from(Item.class);
criteriaQuery.select(itemRoot);
Join<String, Object> properties = itemRoot.join("properties");
// or
//MapJoin<Item, String, Object> properties = itemRoot.joinMap("properties");
Predicate pre1 = cb.equal(properties.get(ITEM_PROPERTY_1), "123");
Predicate pre2 = cb.equal(properties.get(ITEM_PROPERTY_2), "456");
Predicate propertiesPredicate = cb.and(pre1, pre2);
criteriaQuery.where(propertiesPredicate);
Item item = em.createQuery(criteriaQuery).getSingleResult();
But I've read that this is only for associations.
On the join i get an:
IllegalArgumentException: Requested attribute was not a map.
So could sb explain to me, how I will be able to find a key-value pair in a map with the Criteria API?
Edit: I am not able to change anything in the DB.
So I need to guess a little bit because you didn't show us your DB Table, that's why I answer a little bit more freely.
And as a disclaimer: it might be easier and it would be more efficient to query a real table instead of an serialized object/json.
But I would do it this way:
The Table in MSSQL:
create table ${schema}.myTable
(
id bigint identity
constraint PK_myStuff
primary key,
properties nvarchar(max) not null
) go
The Java entity (draft):
public class Item extends AbstractPersistable<...> {
#Column(name = "properties", columnDefinition = "nvarchar")
private String properties;
}
The Java Specification:
protected Specification<Item> customFilter(String filterArg) {
return (root, query, cb) ->
cb.like(root.get(Item_.properties), filterArg);
}
This way your query searches the properties for a string pattern.
Info:
https://vladmihalcea.com/sql-server-json-hibernate/
I have a map
Map<String, String[]> newMetaData = new LinkedHashMap();
which I populate with data like this:
newMetaData.put(
((String) elm.get("companyName")).trim(),
new String[]{
this.storeFile( "logo", (String) elm.get("logoLink") ),
this.storeFile( "profile", (String) elm.get("companyProfile") ),
this.storeFile( "action", (String) elm.get("drash") ),
(String) elm.get("fwtografies")
}
);
StoreFile is a function that returns string. I save this map to the storage as
Storage.getInstance().writeObject("MetaData", newMetaData);
Later on the code I retrive the above map as:
Map<String, String[]> metaData = (Map)Storage.getInstance().readObject("MetaData");
But instead of getting a Map of <String, String[] & gt; I get Map of <String, Object[] >
any help is appreciated
Generics in Java are syntax sugar. They are erased by the compiler and ignored by Codename One for the most part. This:
Map<String, String[]> newMetaData = new LinkedHashMap<>();
Is equivalent to this as far as the generated code is concerned:
Map newMetaData = new LinkedHashMap();
There are some caveats because the first version will also add casts when you use methods like set/put which could trigger a class cast exception in some cases. The thing is that this works in reverse as well so this should work just fine:
Map<String, String[]> metaData = (Map<String, String[]>)Storage.getInstance().readObject("MetaData");
Earlier I had a table named ApplicationConfiguration which simply had [Key],[Value] columns to store some config data. This was queried straight away using SQL queries.
Now I intend to make use of Entity Framework (EF) Code First approach to query this table. The specialty of this table is that the table will have only a fixed number of rows in its lifetime. Only the Value column can be updated.
So as per the code first approach, we have to first write our POCO classes with its properties that will be mapped to columns in the underlying table. However, I wish to have a Dictionary<> structure to represent these configuration KV pairs. My concern is, will EF be able to fire update queries against any updation to the the value of a particular pair.
Also since I am using Code First approach, I would want some seed data(i.e the fixed number of rows and its initial content) to the added after the table itself is created on the fly when the application is first executed.
If Dictionary<> cannot be used, please suggest some alternative. Thanks in advance.
Coded this way:
public class ApplicationConfiguration
{
public int Id { get; set; }
public string Key { get; set; }
public int Value { get; set; } // should be string, but I'm lazy
}
class Context : DbContext
{
internal class ContextInitializer : DropCreateDatabaseIfModelChanges<Context>
{
protected override void Seed(Context context)
{
var defaults = new List<ApplicationConfiguration>
{
new ApplicationConfiguration {Key = "Top", Value = 5},
new ApplicationConfiguration {Key = "Bottom", Value = 7},
new ApplicationConfiguration {Key = "Left", Value = 1},
new ApplicationConfiguration {Key = "Right", Value = 3}
};
// foreach (var c in defaults)
// context.ConfigurationMap.Add(c.Key, c); // by design, no IReadOnlyDictionary.Add
foreach (var c in defaults)
context.ApplicationConfigurations.Add(c);
base.Seed(context);
}
}
public Context()
{
Database.SetInitializer(new ContextInitializer());
}
private IDbSet<ApplicationConfiguration> ApplicationConfigurations
{
get { return Set<ApplicationConfiguration>(); }
}
public IReadOnlyDictionary<string, ApplicationConfiguration> ConfigurationMap
{
get { return ApplicationConfigurations.ToDictionary(kvp => kvp.Key, kvp => kvp); }
}
}
Used this way:
using (var context = new Context())
{
ReadConfigurationOnly(context.ConfigurationMap);
}
using (var context = new Context())
{
ModifyConfiguration(context.ConfigurationMap);
context.SaveChanges();
}
static void ReadConfigurationOnly(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
foreach (var k in configuration.Keys)
Console.WriteLine("{0} = {1}", k, configuration[k].Value);
}
static void ModifyConfiguration(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
foreach (var k in configuration.Keys)
configuration[k].Value++; // this is why I was lazy, using an int for a string
}
So, I wrote it up this way — using an int Value property rather than a string — just so I could run the "Used this way" code over and over, and see the database update each time, without having to come up with some other way to change Value in an interesting way.
It's not quite as nifty here to use a IReadOnlyDictionary<string, ApplicatonConfiguration> instead of a IReadOnlyDictionary<string, string>, the way we'd really like, but that's more than made up for by the fact that we can easily modify our collection values without resorting to a clumsier Set method taking a dictionary as input. The drawback, of course, is that we have to settle for configuration[key].Value = "new value" rather than configuration[key] = "new value", but — as I say — I think it's worth it.
EDIT
Dang! I wrote this code up specifically to answer this question, but I think I like it so much, I'm going to add it to my bag of tricks ... this would fit in really well when my company goes from local databases to Azure instances in the cloud, and the current app.config has to go into the database.
Now all I need is a ContextInitializer taking a System.Configuration.ConfigurationManager as a ctor parameter in order to seed a new database from an existing app.config ...
I don't think you can map a table directly to a Dictionary; you will probably have to write your own wrapper to fill a dictionary from the table and update it back to the DB. Entities are each a row of a given table... Something like this (untested):
public Dictionary<string, string> GetDictionary()
{
Dictionary<string, string> dic = new Dictionary<string, string>();
using (var db = new Context())
{
var configs = db.ApplicationConfiguration.Select();
foreach (var entry in configs)
{
dic.Add(config.Key, config.Value);
}
}
return dic;
}
public void SaveConfig(Dictionary<string, string> dic)
{
using (var db = new Context())
{
foreach (KeyValuePair kvp in dic)
{
if (!db.ApplicationConfiguration.First(a => a.Key == kvp.Key).Value == kvp.Value)
{
var ac = new ApplicationConfiguration();
ac.Key = kvp.Key;
ac.Value = kvp.Value;
db.Entry(ac).State = EntityState.Modified;
}
}
db.SaveChanges();
}
}
For your second question, you want to use the Seed() method to add initial values to the database. See here for an example implementation.
I use a similar style of code many times in my application to read in records from a database
WorkoutResultsRecord is inherited from a class called BaseRecord. One of the base constructors takes a IDataReader parameter to read fields into the class (as seen below).
What I want to do is define a generic function that will do the following for any/all of my 60+ xxxRecord type classes ie I can pass in a type as a parameter and it will return the correct type of objects as a typed List. Is it possible with Activator class? I've not used it before and my results just wouldn't compile
protected List<WorkoutResultsRecord> ReadRecordList(string sql,
IDbConnection connection)
{
var results = new List<WorkoutResultsRecord>();
using (IDbCommand command = GetCommand(sql, connection))
using (IDataReader reader = command.ExecuteReader())
while (reader.Read())
results.Add(new WorkoutResultsRecord(reader));
return results;
}
My really bad, failed attempt :(
private void sfsdf(Type type)
{
List<typeof(type)> lst = new List<type>();
Activator.CreateInstance(List<typeof(type)>);
}// function
this should work:
private void sfsdf(Type type)
{
Type genericType = typeof(List<>).MakeGenericType(type);
System.Collections.IList theList = (IList) Activator.CreateInstance(genericType);
// do whatever you like with this list...
}
Note: as the type is known at runtime only, it's not possible for you to declare a List when you write the code, so rather, use IList interface instead, but the created object theList should be of the expected type...
The following is the full function with all the generics done as I wanted. This will save a lot of typing!! thanks very much
allResults = (List<WorkoutResultsRecord>)FillList(typeof(WorkoutResultsRecord),
sql, connection, new KVP("FROMDATE", fromUtf.Date),
new KVP("TODATE", endDate.AddDays(1).Date));
IList FillList(Type type,string sql,IDbConnection connection,
params KVP[] parameters)
{
Type genericType = typeof(List<>).MakeGenericType(type);
IList results = (IList)Activator.CreateInstance(genericType);
using (var command= Command(sql,connection))
{
foreach(KVP parameter in parameters)
CreateParam(command,parameter.Key,parameter.Value);
using (IDataReader reader = command.ExecuteReader())
while (reader.Read())
results.Add(Activator.CreateInstance(type,reader));
}
return results;
}
I was trying to parse this using System.Json but I'm a bit confused how to use LINQ on this. The JSON string returned was something like:
{"SearchResults":[{"PageCount":"1"},
{"SEARCHVAL":"Result","CATEGORY":"Category1","X":"1","Y":"2"},
{"SEARCHVAL":"AnotherResult","CATEGORY":"Category1","X":"2","Y":"2"}]}
My code starts out as:
WebClient client = new WebClient();
client.OpenReadCompleted +=
new OpenReadCompletedEventHandler(client_OpenReadCompleted);
client.OpenReadAsync(uri);
then proceeds to the event handler where the result is stored in a Stream. Any example how to get just the SEARCHVALs, CATEGORYs, Xs and Ys from the stream using LINQ?
The DataContractJSonSerializer is one way to do it but the presence of the "PageCount" object makes its use awkard. It looks to me as though someone has goofed on the server end, it would make much more sense to have the page count a property of the outer object and therefore leave the search results array to contain an homogeneous set of objects.
The System.Json namespace is handy for this sort of JSON since its very flexible.
JsonObject searchResults = (JsonObject)JsonValue.Load(e.Result);
Now you could use some Linq to get a set of objects out of this but first so we need to create a DTO type for it:-
public class SearchResult
{
public string SearchValue {get; set;}
public string Category {get; set;}
public string X {get; set;}
public string Y {get; set;}
}
So now the results query is:-
var resultList = ((JsonArray)searchResults["SearchResults"])
.OfType<JsonObject>()
.Where(o => o.ContainsKey("SEARCHVAL"))
.Select(o => new SearchResult() {
SearchValue = o["SEARCHVALUE"],
Category = o["CATEGORY"].
X = o["X"],
Y = o["Y"]
}).ToList();
The resultList is List<SearchResult> which you can now bind into some Xaml for display.