How to access a key named with asterisk? - c#

There is a method in Wikimedia API that gives a localized title.
Examples:
Cloud:
http://en.wikipedia.org/w/api.php?format=json&action=query&titles=Cloud&prop=langlinks&lllimit=500&lllang=ru&continue=
Rain: http://en.wikipedia.org/w/api.php?format=json&action=query&titles=Rain&prop=langlinks&lllimit=500&lllang=ru&continue=
Cloud response:
{
"batchcomplete":"",
"query":{
"pages":{
"47515":{
"pageid":47515,
"ns":0,
"title":"Cloud",
"langlinks":[
{
"lang":"ru",
"*":"\u041e\u0431\u043b\u0430\u043a\u0430"
}
]
}
}
}
}
Rain response:
{
"batchcomplete":"",
"query":{
"pages":{
"19009110":{
"pageid":19009110,
"ns":0,
"title":"Rain",
"langlinks":[
{
"lang":"ru",
"*":"Дождь"
}
]
}
}
}
}
Important note: integer container under pages (e.g. 19009110) is always different, because it equals page id.
C# code:
dynamic datacontainer_RUname2 = JObject.Parse(cleanJson_string_RUname);
String localizedName = datacontainer_RUname.[HERE SHOULD BE *];
How can I access a key named with asterisk '*'?

string content;
using (var webClient = new WebClient())
{
const string url = "http://en.wikipedia.org/w/api.php?format=json&action=query&titles=Cloud&prop=langlinks&lllimit=500&lllang=ru&continue=";
content = webClient.DownloadString(url);
}
var obj = JObject.Parse(content);
var query = obj["query"];
var pages = query["pages"].Value<JObject>();
var page = pages.PropertyValues().First();
var langLinks = page["langlinks"].Values<JObject>();
var firstLangLink = langLinks.First();
var localizedName = firstLangLink["*"];
See a working demo with live data.

Just use the normal indexing on the object.
string localizedName = obj["*"];
In your case... to get to your object, you can do this query in both cases. To collect all links returned from the query:
var allLinks =
from page in response.SelectToken("query.pages").Values()
from link in page["langlinks"]
select (string)link["*"];

Related

Is it possible to get a batch of text content through Azure DevOps REST API?

I need to get (not download) the content from 10.000~ manifest files within a project in Azure DevOps, but I don't manage to achieve this. I have found several ways to retrieve the content from one file at a time, but in this context, it is neither an efficient nor sustainable solution. I have managed to retrieve all files of a particular file type by checking if the file path ends with the name of the file, then using the TfvcHttpClientBase.GetItemsBatch method. However, this method does not return the item's content.
Program.cs
using Microsoft.TeamFoundation.SourceControl.WebApi;
AzureRest azureRest = new AzureRest();
var tfvcItems = azureRest.GetTfvcItems();
List<TfvcItemDescriptor> itemDescriptorsList = new List<TfvcItemDescriptor>();
foreach(var item in tfvcItems)
{
//Example manifest file .NET
if (item.Path.EndsWith("packages.config"))
{
var itemDescriptor = new TfvcItemDescriptor()
{
Path = item.Path,
RecursionLevel = VersionControlRecursionType.None,
Version = "",
VersionOption = TfvcVersionOption.None,
VersionType = TfvcVersionType.Latest
};
itemDescriptorsList.Add(itemDescriptor);
}
}
TfvcItemDescriptor[] itemDescriptorsArray = itemDescriptorsList.ToArray();
var itemBatch = azureRest.GetTfvcItemsBatch(itemDescriptorsArray);
foreach(var itemList in itemBatch)
{
foreach(var itemListList in itemList)
{
Console.WriteLine("Content: " + itemListList.Content); //empty/null
Console.WriteLine("ContentMetadata: " + itemListList.ContentMetadata); //not empty/null
}
}
AzureRest.cs
using Microsoft.TeamFoundation.SourceControl.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
public class AzureRest
{
const string ORG_URL = "https://org/url/url";
const string PROJECT = "Project";
const string PAT = "PersonalAccessToken";
private string GetTokenConfig()
{
return PAT;
}
private string GetProjectNameConfig()
{
return PROJECT;
}
private VssConnection Authenticate()
{
string token = GetTokenConfig();
string projectName = GetProjectNameConfig();
var credentials = new VssBasicCredential(string.Empty, token);
var connection = new VssConnection(new Uri(ORG_URL), credentials);
return connection;
}
public List<TfvcItem> GetTfvcItems()
{
var connection = Authenticate();
using (TfvcHttpClient tfvcClient = connection.GetClient<TfvcHttpClient>())
{
var tfvcItems = tfvcClient.GetItemsAsync(scopePath: "/Path", recursionLevel: VersionControlRecursionType.Full, true).Result;
return tfvcItems;
}
}
public List<List<TfvcItem>> GetTfvcItemsBatch(TfvcItemDescriptor[] itemDescriptors)
{
TfvcItemRequestData requestData = new TfvcItemRequestData()
{
IncludeContentMetadata = true,
IncludeLinks = true,
ItemDescriptors = itemDescriptors
};
var connection = Authenticate();
using (TfvcHttpClient tfvcClient = connection.GetClient<TfvcHttpClient>())
{
var tfvcItems = tfvcClient.GetItemsBatchAsync(requestData).Result;
return tfvcItems;
}
}
}
}
For reference:
I have tested the codes you shared and when debugging at "itemDescriptorsList" and have found that there is no content specified in it, so that's why you cannot get the txt content.
You should first check and add the content property into the "itemDescriptorsList".

PowerBIAPI - Operation is not supported for selector # - connection details contains parameters" - update ServerName in PowerBI Dataset of the Report

I have a Problem Updating the Server details in the dataset of the uploaded power bi Report. Please Help.
Here I used 2 approaches.
Approach 1
Used the below method in Microsoft.PowerBI.Api.V2
UpdateDatasourcesInGroup
public static void UpdateSqlDatabaseConnectionString(string WorkspaceId, string DatasetId, string Server, string Database)
{
var tokenCredentials = GetTokenCredentials();
using (var pbiClient = new PowerBIClient(new Uri(ApiUrl), tokenCredentials.Item1))
{
Datasource targetDatasource = pbiClient.Datasets.GetDatasourcesInGroup(WorkspaceId, DatasetId).Value.First();
string currentServer = targetDatasource.ConnectionDetails.Server;
string currentDatabase = targetDatasource.ConnectionDetails.Database;
if (Server.ToLower().Equals(currentServer.ToLower()) && Database.ToLower().Equals(currentDatabase.ToLower()))
{
Console.WriteLine("New server and database name are the same as the old names");
return;
}
DatasourceConnectionDetails connectionDetails = new DatasourceConnectionDetails
{
Database = Database,
Server = Server
};
UpdateDatasourceConnectionRequest updateConnRequest =
new UpdateDatasourceConnectionRequest
{
DatasourceSelector = targetDatasource,
ConnectionDetails = connectionDetails
};
UpdateDatasourcesRequest updateDatasourcesRequest = new UpdateDatasourcesRequest(updateConnRequest);
pbiClient.Datasets.UpdateDatasourcesInGroup(WorkspaceId, DatasetId, updateDatasourcesRequest);
}
}
Captured the request in fiddler
Request:
{
"updateDetails": [
{
"connectionDetails": {
"server": "OldServer",
"database": "OldDatabase"
},
"datasourceSelector": {
"datasourceType": "Sql",
"connectionDetails": {
"server": "NewServer",
"database": "NewDatabase"
},
"gatewayId": "gatewayId",
"datasourceId": "datasourceId"
}
}
]
}
Response:
{"error":{"code":"InvalidRequest","message":"Operation is not supported for selector # - connection details contains parameters"}}
Approach 2
Called the Power BI rest API
public static void UpdateServerName_RestAPI(string groupId, string datasetId)
{
var tokenCredentials = InitPowerBI_New();
HttpResponseMessage response;
try
{
var httpClient = new HttpClient();
// Add AccessToken in header
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenCredentials.Item2);
var url = $"https://api.powerbi.com/v1.0/myorg/groups/{groupId}/datasets/{datasetId}/Default.UpdateDatasources";
var form = prepareJsonForUpdateServerDetails();
var content = new StringContent(form, Encoding.UTF8, "application/json");
response = httpClient.PostAsync(url, content).Result;
response.EnsureSuccessStatusCode();
httpClient.Dispose();
}
catch (Exception)
{
}
}
Request :
{
"UpdateDetails":[
{
"datasourceSelector":{
"datasourceType":"Sql",
"connectionDetails":{
"server":"OldServer",
"database":"OldDatabase"
}
},
"connectionDetails":{
"server":"NewServer",
"database":"NewDatabase"
}
}
]
}
Response:
{"error":{"code":"InvalidRequest","message":"Operation is not supported for selector # - connection details contains parameters"}}
Please Help.
Thank You
This happens when the report queries connect to a database using a parameter. In this case you cannot update the connection, rather update the parameters or remove the connection from the query.
So, if you see in your query code something like Sql.Database(#"ServerName",... you must use Client.Datasets.UpdateParameters and pass in for details:
UpdateMashupParameterDetails request = new()
{
Name = "ServerName",
NewValue = "newServerName"
};
and similarly for database.
Further details https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/update-parameters

Json child access yahoo weather json in C#

So I'm trying to get the weather from the Yahoo's weather Json, but the thing is I keep getting this error
{"Cannot access child value on Newtonsoft.Json.Linq.JValue."}
Right now I have no idea why is this happening. I checked the parenting a few times already, the spelling and all that.
public String GetWeather() {
StringBuilder theWebAddress = new StringBuilder();
theWebAddress.Append("https://query.yahooapis.com/v1/public/yql?");
theWebAddress.Append("q=" + System.Web.HttpUtility.UrlEncode("select * from weather.forecast where woeid in (select woeid from geo.places(1) where text='"+ city + ", "+ state + "') and u='" + units +"'"));
theWebAddress.Append("&format=json");
theWebAddress.Append("&diagnostics=false");
string results = "";
using (WebClient wClient = new WebClient())
{
results = wClient.DownloadString(theWebAddress.ToString());
}
JObject dataObject = JObject.Parse(results);
JArray jsonArray = (JArray)dataObject["query"]["results"]["channel"]; //This is the line that is generating the error.
foreach (var woeid in jsonArray)
{
//stocheaza informatiile in variabile
condition = woeid["item"]["condition"]["text"].ToString();
//System.Diagnostics.Debug.WriteLine(condition);
return condition;
}
return null;
}
The link to the API is here. So as far as I see, there is problem with getting the child of query or results. Any ideas? Thanks in advance.
I solved it by changing the code. Instead of using that code, I changed it with this
public string GetWeather(string info)
{
string results = "";
using (WebClient wc = new WebClient())
{
results = wc.DownloadString("https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%27galati%2C%20ro%27)%20and%20u%3D%22c%22&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys");
}
dynamic jo = JObject.Parse(results);
if (info == "cond")
{
var items = jo.query.results.channel.item.condition;
condition = items.text;
return condition;
}
}
Now it works as intended.

Load text from web during xml parsing in Windows 8 Async

I have a unique issue, I want to get the name of an application from it's AppID while I convert an XML file into objects. This is the code I'm using at present:
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = GetName(query.Value).ToString(),
};
itemGridView.DataContext = data;
}
This is the code I'm using to convert the GUID into an app name using Microsoft's Store API. I can confirm that it does return the app name. I'm just unsure how I can get this to display.
private async Task<string> GetName(string guid)
{
try
{
string link = "https://services.apps.microsoft.com/browse/6.2.9200-1/615/en-NZ_en-NZ/c/NZ/cp/10005001/Apps/{0}";
string url = string.Format(link, guid);
var httpClient = new HttpClient();
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.SendAsync(httpRequestMessage);
var xmlString = await response.Content.ReadAsStringAsync();
XmlDocument NameXML = new XmlDocument();
NameXML = await XmlDocument.LoadFromUriAsync(new Uri(url));
string sAppName = NameXML.GetElementsByTagName("T")[0].ChildNodes[0].NodeValue.ToString();
return sAppName;
}
catch(Exception)
{
return guid;
}
}
I think my problem is with the async / await tasks. I've just been exposed to it now... how would I load up the App Name alongside the AppID when I parse the xml file?
The output that's being displayed when I run the app is "System.Threading.Tasks.Task[System.String]" (The objects load and the links and everything works fine, its just that the above is displayed instead of the app name).
I've been debugging using breakpoints, it appears that the GetName method only seems to be triggered later on, I'm not sure however.
Try to change this line :
AppName = GetName(query.Value).ToString(),
To this :
AppName = await GetName(query.Value),
GetName will return Task<string> instead of string when not awaited. And the method where above code resides required to be async because of using await inside that method :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var data = from query in xdoc.Descendants("AppID")
select new APP
{
AppID = query.Value,
AppName = await GetName(query.Value),
};
itemGridView.DataContext = data;
}
....
}
UPDATE :
As you already noticed, LINQ has very limited support for async/await currently. So to workaround this limitation, we can use normal for loop to avoid calling async function inside LINQ :
private async void SomeMethod()
{
....
if (xdoc.Element("Application") != null)
{
var query = from query in xdoc.Descendants("AppID")
select query.Value;
var data = new List<App>();
foreach (var q in query)
{
data.Add(new App{ AppId = q, AppName = await GetName(q) });
}
itemGridView.DataContext = data;
}
....
}

C# Google drive sdk. How to get a list of google drive folders?

I'm writing a program to allow a user to upload files to their Google Drive account. I have the upload part working and am using OAuth2. The issue I'm currently having is getting a list of folders from the users Drive account.
I found some code that is supposed to do this using the .setUserCredentials method, but it doesn't work:
DocumentsService service1 = new DocumentsService("project");
service1.setUserCredentials("user","pass");
FolderQuery query1 = new FolderQuery();
// Make a request to the API and get all documents.
DocumentsFeed feed = service1.Query(query1);
// Iterate through all of the documents returned
foreach (DocumentEntry entry in feed.Entries)
{
var blech = entry.Title.Text;
}
Nothing is returned. Ideally, I want to use OAuth2 to do this. I've been trying with the following code, trying to set the authentication token, but I always get denied access:
String CLIENT_ID = "clientid";
String CLIENT_SECRET = "secretid";
var docprovider = new NativeApplicationClient(GoogleAuthenticationServer.Description, CLIENT_ID, CLIENT_SECRET);
var docstate = GetDocAuthentication(docprovider);
DocumentsService service1 = new DocumentsService("project");
service1.SetAuthenticationToken(docstate.RefreshToken);
FolderQuery query1 = new FolderQuery();
DocumentsFeed feed = service1.Query(query1); //get error here
// Iterate through all of the documents returned
foreach (DocumentEntry entry in feed.Entries)
{
// Print the title of this document to the screen
var blech = entry.Title.Text;
}
..
private static IAuthorizationState GetDocAuthentication(NativeApplicationClient client)
{
const string STORAGE = "storagestring";
const string KEY = "keystring";
string scope = "https://docs.google.com/feeds/default/private/full/-/folder";
// Check if there is a cached refresh token available.
IAuthorizationState state = AuthorizationMgr.GetCachedRefreshToken(STORAGE, KEY);
if (state != null)
{
try
{
client.RefreshToken(state);
return state; // Yes - we are done.
}
catch (DotNetOpenAuth.Messaging.ProtocolException ex)
{
}
}
// Retrieve the authorization from the user.
state = AuthorizationMgr.RequestNativeAuthorization(client, scope);
AuthorizationMgr.SetCachedRefreshToken(STORAGE, KEY, state);
return state;
}
Specifically, I get "Execution of request failed: https://docs.google.com/feeds/default/private/full/-/folder - The remote server returned an error: (401) Unauthorized".
I've also tried:
var docauth = new OAuth2Authenticator<NativeApplicationClient>(docprovider, GetDocAuthentication);
DocumentsService service1 = new DocumentsService("project");
service1.SetAuthenticationToken(docauth.State.AccessToken);
but "State" is always null, so I get a null object error. What am I doing wrong and how is this done?
You should use the Drive SDK, not the Documents List API, which allows you to list folders. You can use "root" as a folderId if you want to list the root directory.
I actually implemented the v3 version of the GDrive SDK for .NET and needed to search for folders as well.
I prefer requesting uniquely all folders instead of getting all files and then performing a LinQ query to keep just the folders.
This is my implementation:
private async Task<bool> FolderExistsAsync(string folderName)
{
var response = await GetAllFoldersAsync();
return response.Files
.Where(x => x.Name.ToLower() == folderName.ToLower())
.Any();
}
private async Task<Google.Apis.Drive.v3.Data.FileList> GetAllFoldersAsync()
{
var request = _service.Files.List();
request.Q = "mimeType = 'application/vnd.google-apps.folder'";
var response = await request.ExecuteAsync();
return response;
}
You could request the name on the Q this way as well:
request.Q = $"mimeType = 'application/vnd.google-apps.folder' and name = '{folderName}'";
Which would lead and simplify things to (obviating null checking):
private async Task<bool> FolderExistsAsync(string folderName)
{
var response = await GetDesiredFolder(folderName);
return response.Files.Any();
}
private async Task<FileList> GetDesiredFolder(string folderName)
{
var request = _service.Files.List();
request.Q = $"mimeType = 'application/vnd.google-apps.folder' and name = '{folderName}'";
var response = await request.ExecuteAsync();
return response;
}
private IEnumerable<DocumentEntry> GetFolders(string id) {
if (IsLogged) {
var query = new FolderQuery(id)
{
ShowFolders = true
};
var feed = GoogleDocumentsService.Query(query);
return feed.Entries.Cast<DocumentEntry>().Where(x => x.IsFolder).OrderBy(x => x.Title.Text);
}
return null;
}
...
var rootFolders = GetFolders("root");
if (rootFolders != null){
foreach(var folder in rootFolders){
var subFolders = GetFolders(folder.ResourceId);
...
}
}
where GoogleDocumentsService is a instance of DocumentsService and IsLogged is a success logged flag.
I got this way to get list of folders from google drive
FilesResource.ListRequest filelist= service.Files.List();
filelist.Execute().Items.ToList().Where(x => x.MimeType == "application/vnd.google-apps.folder").ToList()

Categories

Resources