XmlException When Parsing Valid XML - c#

I have a WPF application that calls an API and creates an System.Xml.Linq.XDocument using XDocument.Parse(string). I am running into a problem where an XmlException ("Root element is missing") is thrown when I try to do this, but my XML is completely valid. I tried syntax-checking it by calling the API in my browser and checking its syntax, calling the API in my application and Console.WriteLineing the response, and using various XML syntax validators (all of which returned no errors).
A sample XML response from the API is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<event title="Event 1" id="75823347" icon="www.example.com/images/event1-icon.png" uri="www.example.com/rsvp/event1" mode="none" price="10.00" cover="www.example.com/event1-cover.png" enddate="2016-06-01 14:00:00" startdate="2016-06-01 12:00:00" address="1 Example St, Example City State 12345" location="Example Place" description="This is an event" shortdescription="This is an event" theme="auto" color="#FF000000"/>
</response>
This is my application's code:
public static WebRequest CreateRequest(string baseUrl, string httpMethod, Dictionary<string, string> requestValues) {
var requestItems = requestValues == null ? null : requestValues.Select(pair => string.Format("&{0}={1}", pair.Key, pair.Value));
var requestString = "";
if (requestItems != null)
foreach (var s in requestItems)
requestString += s;
var request = WebRequest.CreateHttp(baseUrl + CredentialRequestString + requestString);
request.Method = httpMethod.ToUpper();
request.ContentType = "application/x-www-form-urlencoded";
request.Credentials = CredentialCache.DefaultCredentials;
return request;
}
public static WebRequest CreateRequest(string apiEndpoint, string endpointParam, int apiVersion, string httpMethod, Dictionary<string, string> requestValues) {
return CreateRequest(string.Format("http://www.example.com/api/v{0}/{1}/{2}", apiVersion, apiEndpoint, endpointParam), httpMethod, requestValues);
}
public static async Task<string> GetResponseFromServer(WebRequest request) {
string s;
using (var response = await request.GetResponseAsync()) {
using (var responseStream = response.GetResponseStream()) {
using (var streamReader = new StreamReader(responseStream)) {
s = streamReader.ReadToEnd();
}
}
}
return s;
}
public static async Task<List<Event>> GetEvents() {
var response = await GetResponseFromServer(CreateRequest("events", "", 1, "GET", null));
Console.WriteLine(response); //validation
var data = XDocument.Parse(response).Root; //XmlException: Root element is mising
return new List<Event>(data.Elements("event").Select(e => Event.FromXml(e.Value)));
}
Why is this happening?

The following code works
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
var data = XDocument.Load(FILENAME);
Event _event = Event.FromXml(data.Descendants("event").FirstOrDefault());
}
}
public class Event
{
public string title { get ; set; }
public string icon {get; set; }
public string uri { get; set; }
public string mode { get;set; }
public decimal price { get; set; }
public string cover { get; set; }
public DateTime enddate { get; set; }
public DateTime startdate { get; set; }
public string address { get; set; }
public string location { get; set; }
public string description { get; set; }
public string shortdescription { get; set; }
public string theme { get; set; }
public uint color { get; set; }
public static Event FromXml(XElement data)
{
Event _event = new Event();
_event.title = (string)data.Attribute("title");
_event.icon = (string)data.Attribute("icon");
_event.uri = (string)data.Attribute("uri");
_event.mode = (string)data.Attribute("mode");
_event.price = (decimal)data.Attribute("price");
_event.cover = (string)data.Attribute("cover");
_event.enddate = (DateTime)data.Attribute("enddate");
_event.startdate = (DateTime)data.Attribute("startdate");
_event.address = (string)data.Attribute("address");
_event.location = (string)data.Attribute("location");
_event.description = (string)data.Attribute("description");
_event.shortdescription = (string)data.Attribute("shortdescription");
_event.theme = (string)data.Attribute("theme");
_event.color = uint.Parse(data.Attribute("color").Value.Substring(1), System.Globalization.NumberStyles.HexNumber) ;
return _event;
}
}
}

Related

How to implement AppCenter Push API?

IMPORTANT - Microsoft is retiring AppCenter Push pretty soon, you can still follow my answer below to implement it. Or you can follow my new post at How to implement Push Notification in Xamarin with Firebase and Apple Push Notification with C# backend on using Firebase & Apple Push Notification. Thank you.
I have been reading https://learn.microsoft.com/en-us/appcenter/push/rest-api and looking over the Internet for example of how to implement this easily but found nothing useful.
I read and implemented this
https://www.andrewhoefling.com/Blog/Post/push-notifications-with-app-center-api-integration. His solution offers very good start, but incomplete.
So I enhanced Andrew Hoefling's solution from above to a full working version and thought it's good to share with fellows Xamarin members in the answer below.
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<Guid, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach(Guid key in dicInstallIdPlatform.Keys)
{
switch(dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "{FULL ACCESS TOKEN}";
public const string DeviceTarget = "devices_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "{APPNAME_ANDROID}";
public const string AppNameIOS = "{APPNAME_IOS}";
public const string Organization = "{ORG_OR_USER}";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("devices")]
public IEnumerable Devices { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if(customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.DeviceTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Devices = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameiOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Devices = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
To use this, simply do the following from your program
var receiptInstallID = new Dictionary<string, string>
{
{ "XXX-XXX-XXX-XXX", "Android" },
{ "YYY-YYY-YYY-YYY", "iOS" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("{YOUR_TITLE}", "{YOUR_MESSAGE}", null);
Can't add comment yet, but theres a typo in the above answer
string URL = $"{Constants.Url}/{Constants.Organization}/Constants.AppNameiOS}/{Constants.Apis.Notification}";
Should be
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
Missing { and IOS constant is capitalized.
Also, in your example to call it, Should be constructed as < guid, string >
var receiptInstallID = new Dictionary<Guid, string>
also needed as just FYI:
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
If you want to send notification for target user (user_ids_target),
public class AppCenterPush
{
User receiver = new User();
public AppCenterPush(Dictionary<string, string> dicInstallIdPlatform)
{
//Simply get all the Install IDs for the receipient with the platform name as the value
foreach (string key in dicInstallIdPlatform.Keys)
{
switch (dicInstallIdPlatform[key])
{
case "Android":
receiver.AndroidDevices.Add(key.ToString());
break;
case "iOS":
receiver.IOSDevices.Add(key.ToString());
break;
}
}
}
public class Constants
{
public const string Url = "https://api.appcenter.ms/v0.1/apps";
public const string ApiKeyName = "X-API-Token";
//Push required to use this. Go to https://learn.microsoft.com/en-us/appcenter/api-docs/index for instruction
public const string FullAccessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
public const string DeviceTarget = "devices_target";
public const string UserTarget = "user_ids_target";
public class Apis { public const string Notification = "push/notifications"; }
//You can find your AppName and Organization/User name at your AppCenter URL as such https://appcenter.ms/users/{owner-name}/apps/{app-name}
public const string AppNameAndroid = "XXXXXX";
public const string AppNameIOS = "XXXXXX";
public const string Organization = "XXXXXXX";
}
[JsonObject]
public class Push
{
[JsonProperty("notification_target")]
public Target Target { get; set; }
[JsonProperty("notification_content")]
public Content Content { get; set; }
}
[JsonObject]
public class Content
{
public Content()
{
Name = "default"; //By default cannot be empty, must have at least 3 characters
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("custom_data")]
public IDictionary<string, string> CustomData { get; set; }
}
[JsonObject]
public class Target
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("user_ids")]
public IEnumerable Users { get; set; }
}
public class User
{
public User()
{
IOSDevices = new List<string>();
AndroidDevices = new List<string>();
}
public List<string> IOSDevices { get; set; }
public List<string> AndroidDevices { get; set; }
}
public async Task<bool> Notify(string title, string message, Dictionary<string, string> customData = default(Dictionary<string, string>))
{
try
{
//title, message length cannot exceed 100 char
if (title.Length > 100)
title = title.Substring(0, 95) + "...";
if (message.Length > 100)
message = message.Substring(0, 95) + "...";
if (!receiver.IOSDevices.Any() && !receiver.AndroidDevices.Any())
return false; //No devices to send
//To make sure in Android, title and message is retain when click from notification. Else it's lost when app is in background
if (customData == null)
customData = new Dictionary<string, string>();
if (!customData.ContainsKey("Title"))
customData.Add("Title", title);
if (!customData.ContainsKey("Message"))
customData.Add("Message", message);
//custom data cannot exceed 100 char
foreach (string key in customData.Keys)
{
if (customData[key].Length > 100)
{
customData[key] = customData[key].Substring(0, 95) + "...";
}
}
var push = new Push
{
Content = new Content
{
Title = title,
Body = message,
CustomData = customData
},
Target = new Target
{
Type = Constants.UserTarget
}
};
HttpClient httpClient = new HttpClient();
//Set the content header to json and inject the token
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyName, Constants.FullAccessToken);
//Needed to solve SSL/TLS issue when
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
if (receiver.IOSDevices.Any())
{
push.Target.Users = receiver.IOSDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameIOS}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
if (receiver.AndroidDevices.Any())
{
push.Target.Users = receiver.AndroidDevices;
string content = JsonConvert.SerializeObject(push);
HttpContent httpContent = new StringContent(content, Encoding.UTF8, "application/json");
string URL = $"{Constants.Url}/{Constants.Organization}/{Constants.AppNameAndroid}/{Constants.Apis.Notification}";
var result = await httpClient.PostAsync(URL, httpContent);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
}
}
After create a function for call it;
public async void PushNotification()
{
var receiptInstallID = new Dictionary<string, string>
{
{"17593989838", "Android" }
};
var customData = new Dictionary<string, string>
{
{"taskId", "1234" }
};
AppCenterPush appCenterPush = new AppCenterPush(receiptInstallID);
await appCenterPush.Notify("Hello", "How are you?", customData);
}

WPF application authentication with Google

I have found many different solutions with OAuth and either with some libraries or with pure requests (https://github.com/googlesamples/oauth-apps-for-windows).
However, none of the solutions looks like the one that I really need. Currently, my application uses its own database for users to log in using WCF service request (with username and password). However, all users have their domain e-mail created with Google accounts, so I want to add another button "Sign In with Google", which will just make sure that user can also login with his Google e-mail-password pair. I don't need a returned token for the further use etc.
What is the simplest way to achieve this functionality in WPF/C# desktop application?
Here is a self-sufficient, 3rd party-free, WPF sample that does Google auth (it could be easily converted to winforms as well).
If you run it you won't be logged and the app will show you a button. If you click that button, an embedded webbrowser control will run though the Google auth.
Once you are authenticated, the app will just display your name as returned by Google.
Note it's based on official Google's sample here: https://github.com/googlesamples/oauth-apps-for-windows but it uses an embedded browser instead of spawning an external one (and few other differences).
XAML code:
<Window x:Class="GoogleAuth.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="750" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="btv" />
</Window.Resources>
<Grid>
<DockPanel Visibility="{Binding State.IsSigned, Converter={StaticResource btv}}">
<Label>You are signed as:</Label>
<Label Content="{Binding State.Token.Name}" />
</DockPanel>
<Grid Visibility="{Binding State.IsNotSigned, Converter={StaticResource btv}}">
<Grid.RowDefinitions>
<RowDefinition Height="23" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Click="Button_Click">Click to Sign In</Button>
<WebBrowser Grid.Row="1" x:Name="Wb" Height="Auto" />
</Grid>
</Grid>
</Window>
C# code:
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
namespace GoogleAuth
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
State = new OAuthState();
DataContext = this;
}
public OAuthState State { get; }
private void Button_Click(object sender, RoutedEventArgs e)
{
var thread = new Thread(HandleRedirect);
thread.Start();
}
private async void HandleRedirect()
{
State.Token = null;
// for example, let's pretend I want also want to have access to WebAlbums
var scopes = new string[] { "https://picasaweb.google.com/data/" };
var request = OAuthRequest.BuildLoopbackRequest(scopes);
var listener = new HttpListener();
listener.Prefixes.Add(request.RedirectUri);
listener.Start();
// note: add a reference to System.Windows.Presentation and a 'using System.Windows.Threading' for this to compile
await Dispatcher.BeginInvoke(() =>
{
Wb.Navigate(request.AuthorizationRequestUri);
});
// here, we'll wait for redirection from our hosted webbrowser
var context = await listener.GetContextAsync();
// browser has navigated to our small http servern answer anything here
string html = string.Format("<html><body></body></html>");
var buffer = Encoding.UTF8.GetBytes(html);
context.Response.ContentLength64 = buffer.Length;
var stream = context.Response.OutputStream;
var responseTask = stream.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
stream.Close();
listener.Stop();
});
string error = context.Request.QueryString["error"];
if (error != null)
return;
string state = context.Request.QueryString["state"];
if (state != request.State)
return;
string code = context.Request.QueryString["code"];
State.Token = request.ExchangeCodeForAccessToken(code);
}
}
// state model
public class OAuthState : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private OAuthToken _token;
public OAuthToken Token
{
get => _token;
set
{
if (_token == value)
return;
_token = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Token)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSigned)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsNotSigned)));
}
}
public bool IsSigned => Token != null && Token.ExpirationDate > DateTime.Now;
public bool IsNotSigned => !IsSigned;
}
// This is a sample. Fille information (email, etc.) can depend on scopes
[DataContract]
public class OAuthToken
{
[DataMember(Name = "access_token")]
public string AccessToken { get; set; }
[DataMember(Name = "token_type")]
public string TokenType { get; set; }
[DataMember(Name = "expires_in")]
public int ExpiresIn { get; set; }
[DataMember(Name = "refresh_token")]
public string RefreshToken { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public string Picture { get; set; }
[DataMember]
public string Locale { get; set; }
[DataMember]
public string FamilyName { get; set; }
[DataMember]
public string GivenName { get; set; }
[DataMember]
public string Id { get; set; }
[DataMember]
public string Profile { get; set; }
[DataMember]
public string[] Scopes { get; set; }
// not from google's response, but we store this
public DateTime ExpirationDate { get; set; }
}
// largely inspired from
// https://github.com/googlesamples/oauth-apps-for-windows
public sealed class OAuthRequest
{
// TODO: this is a sample, please change these two, use your own!
private const string ClientId = "581786658708-elflankerquo1a6vsckabbhn25hclla0.apps.googleusercontent.com";
private const string ClientSecret = "3f6NggMbPtrmIBpgx-MK2xXK";
private const string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
private const string TokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
private const string UserInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo";
private OAuthRequest()
{
}
public string AuthorizationRequestUri { get; private set; }
public string State { get; private set; }
public string RedirectUri { get; private set; }
public string CodeVerifier { get; private set; }
public string[] Scopes { get; private set; }
// https://developers.google.com/identity/protocols/OAuth2InstalledApp
public static OAuthRequest BuildLoopbackRequest(params string[] scopes)
{
var request = new OAuthRequest
{
CodeVerifier = RandomDataBase64Url(32),
Scopes = scopes
};
string codeChallenge = Base64UrlEncodeNoPadding(Sha256(request.CodeVerifier));
const string codeChallengeMethod = "S256";
string scope = BuildScopes(scopes);
request.RedirectUri = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
request.State = RandomDataBase64Url(32);
request.AuthorizationRequestUri = string.Format("{0}?response_type=code&scope=openid%20profile{6}&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
AuthorizationEndpoint,
Uri.EscapeDataString(request.RedirectUri),
ClientId,
request.State,
codeChallenge,
codeChallengeMethod,
scope);
return request;
}
// https://developers.google.com/identity/protocols/OAuth2InstalledApp Step 5: Exchange authorization code for refresh and access tokens
public OAuthToken ExchangeCodeForAccessToken(string code)
{
if (code == null)
throw new ArgumentNullException(nameof(code));
string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&scope=&grant_type=authorization_code",
code,
Uri.EscapeDataString(RedirectUri),
ClientId,
CodeVerifier,
ClientSecret
);
return TokenRequest(tokenRequestBody, Scopes);
}
// this is not used in this sample, but can be used to refresh a token from an old one
// https://developers.google.com/identity/protocols/OAuth2InstalledApp Refreshing an access token
public OAuthToken Refresh(OAuthToken oldToken)
{
if (oldToken == null)
throw new ArgumentNullException(nameof(oldToken));
string tokenRequestBody = string.Format("refresh_token={0}&client_id={1}&client_secret={2}&grant_type=refresh_token",
oldToken.RefreshToken,
ClientId,
ClientSecret
);
return TokenRequest(tokenRequestBody, oldToken.Scopes);
}
private static T Deserialize<T>(string json)
{
if (string.IsNullOrWhiteSpace(json))
return default(T);
return Deserialize<T>(Encoding.UTF8.GetBytes(json));
}
private static T Deserialize<T>(byte[] json)
{
if (json == null || json.Length == 0)
return default(T);
using (var ms = new MemoryStream(json))
{
return Deserialize<T>(ms);
}
}
private static T Deserialize<T>(Stream json)
{
if (json == null)
return default(T);
var ser = CreateSerializer(typeof(T));
return (T)ser.ReadObject(json);
}
private static DataContractJsonSerializer CreateSerializer(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
var settings = new DataContractJsonSerializerSettings
{
DateTimeFormat = new DateTimeFormat("yyyy-MM-dd'T'HH:mm:ss.fffK")
};
return new DataContractJsonSerializer(type, settings);
}
// https://stackoverflow.com/questions/223063/how-can-i-create-an-httplistener-class-on-a-random-port-in-c/
private static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
private static string RandomDataBase64Url(int length)
{
using (var rng = new RNGCryptoServiceProvider())
{
var bytes = new byte[length];
rng.GetBytes(bytes);
return Base64UrlEncodeNoPadding(bytes);
}
}
private static byte[] Sha256(string text)
{
using (var sha256 = new SHA256Managed())
{
return sha256.ComputeHash(Encoding.ASCII.GetBytes(text));
}
}
private static string Base64UrlEncodeNoPadding(byte[] buffer)
{
string b64 = Convert.ToBase64String(buffer);
// converts base64 to base64url.
b64 = b64.Replace('+', '-');
b64 = b64.Replace('/', '_');
// strips padding.
b64 = b64.Replace("=", "");
return b64;
}
private static OAuthToken TokenRequest(string tokenRequestBody, string[] scopes)
{
var request = (HttpWebRequest)WebRequest.Create(TokenEndpoint);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
byte[] bytes = Encoding.ASCII.GetBytes(tokenRequestBody);
using (var requestStream = request.GetRequestStream())
{
requestStream.Write(bytes, 0, bytes.Length);
}
var response = request.GetResponse();
using (var responseStream = response.GetResponseStream())
{
var token = Deserialize<OAuthToken>(responseStream);
token.ExpirationDate = DateTime.Now + new TimeSpan(0, 0, token.ExpiresIn);
var user = GetUserInfo(token.AccessToken);
token.Name = user.Name;
token.Picture = user.Picture;
token.Email = user.Email;
token.Locale = user.Locale;
token.FamilyName = user.FamilyName;
token.GivenName = user.GivenName;
token.Id = user.Id;
token.Profile = user.Profile;
token.Scopes = scopes;
return token;
}
}
private static UserInfo GetUserInfo(string accessToken)
{
var request = (HttpWebRequest)WebRequest.Create(UserInfoEndpoint);
request.Method = "GET";
request.Headers.Add(string.Format("Authorization: Bearer {0}", accessToken));
var response = request.GetResponse();
using (var stream = response.GetResponseStream())
{
return Deserialize<UserInfo>(stream);
}
}
private static string BuildScopes(string[] scopes)
{
string scope = null;
if (scopes != null)
{
foreach (var sc in scopes)
{
scope += "%20" + Uri.EscapeDataString(sc);
}
}
return scope;
}
// https://developers.google.com/+/web/api/rest/openidconnect/getOpenIdConnect
[DataContract]
private class UserInfo
{
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "kind")]
public string Kind { get; set; }
[DataMember(Name = "email")]
public string Email { get; set; }
[DataMember(Name = "picture")]
public string Picture { get; set; }
[DataMember(Name = "locale")]
public string Locale { get; set; }
[DataMember(Name = "family_name")]
public string FamilyName { get; set; }
[DataMember(Name = "given_name")]
public string GivenName { get; set; }
[DataMember(Name = "sub")]
public string Id { get; set; }
[DataMember(Name = "profile")]
public string Profile { get; set; }
[DataMember(Name = "gender")]
public string Gender { get; set; }
}
}
}
This code uses an embedded Internet Explorer control, but since Google doesn't support old Internet Explorer versions, it's likely that you also need to add some code to play with IE's compatibility features as described here for example: https://stackoverflow.com/a/28626667/403671
You can put that code in App.xaml.cs, like this:
public partial class App : Application
{
public App()
{
// use code from here: https://stackoverflow.com/a/28626667/403671
SetWebBrowserFeatures();
}
}
Note what the webbrowser will display depends entirely on Google, and it can considerably vary depending on the environment like cookies, language, etc.
I think you are missing the point on what the OAuth is, start by reading this and here is a simple description of the flow.
which will just make sure that user can also login with his Google e-mail-password pair, I don't need a returned token for the further use
There is no api for user/pass validation, and the token is the whole point of the OAuth. A user with existing email should be able to login if indeed you registered your application with google. Also i see no point in prior validation of user/pass, just add the button.

c# deserialize mutliple Json into same list

I have a Api link that returns this Json structure. In the code I request this Api link and then deserialize it into a list. This is done without problems. But if the Api returns more than 50 "counts" it creates another page. How do i get around to loop through all pages and add everything to the existing list?
In the case i linked there will be 38 pages. All need to be added to the list.
Call
// spidyApiUrl = http://www.gw2spidy.com/api/v0.9/json/item-search/iron/1
var spidyApi_idByName_result = api_Handler.objFromApi_idToName(spidyApiUrl);
Function with the return
public RootObject objFromApi_idToName(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
var jsonReader = new JsonTextReader(reader);
var serializer = new JsonSerializer();
//return serializer.Deserialize<RootObject>(jsonReader);
RootObject rootObject = serializer.Deserialize<RootObject>(jsonReader);
if (rootObject.count > 0) { return rootObject; }
else { return null; }
}
}
And ofcourse i also have the get; set; classes.
How do I loop through all pages (if mutliple pages exist, which doesnt have to) and add these to the same object list.
You need continue downloading the data until page == last_page
As you get each page of data you then add the new set of results to the original rootObject's results property with AddRange
I also changed the url that gets passed into the function from
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/1
to
http://www.gw2spidy.com/api/v0.9/json/item-search/iron
This allows me to add the page numbers to the url to get all the pages
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/1
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/2
.....
http://www.gw2spidy.com/api/v0.9/json/item-search/iron/38
Code:
public class Result
{
public int data_id { get; set; }
public string name { get; set; }
public int rarity { get; set; }
public int restriction_level { get; set; }
public string img { get; set; }
public int type_id { get; set; }
public int sub_type_id { get; set; }
public string price_last_changed { get; set; }
public int max_offer_unit_price { get; set; }
public int min_sale_unit_price { get; set; }
public int offer_availability { get; set; }
public int sale_availability { get; set; }
public int sale_price_change_last_hour { get; set; }
public int offer_price_change_last_hour { get; set; }
}
public class RootObject
{
public int count { get; set; }
public int page { get; set; }
public int last_page { get; set; }
public int total { get; set; }
public List<Result> results { get; set; }
}
class Program
{
static void Main(string[] args)
{
objFromApi_idToName("http://www.gw2spidy.com/api/v0.9/json/item-search/iron");
}
public static RootObject objFromApi_idToName(string url)
{
RootObject rootObject = null;
RootObject tempRootObject = null;
int page = 1;
do
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + "/" + page);
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
var jsonReader = new JsonTextReader(reader);
var serializer = new JsonSerializer();
//return serializer.Deserialize<RootObject>(jsonReader);
tempRootObject = serializer.Deserialize<RootObject>(jsonReader);
if (rootObject == null)
{
rootObject = tempRootObject;
}
else
{
rootObject.results.AddRange(tempRootObject.results);
rootObject.count += tempRootObject.count;
}
}
page++;
} while (tempRootObject != null && tempRootObject.last_page != tempRootObject.page);
return rootObject;
}
}
Are you using Web API? If so, could you try something like this?
public RootObject objFromApi_idToName(string url)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("<your uri here>");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("<uri extention here>");
if (response.IsSuccessStatusCode)
{
string jsonStr = await response.Content.ReadAsStringAsync();
var deserializedResponse = JsonConvert.DeserializeObject<List<your model class here>>(jsonStr);
return deserializedResponse;
}
}

Web HttpRequest method in asp.net

My C# class
public class City
{
public int City_id { get; set; }
public string City_name { get; set; }
}
public class Model
{
public City[] lstCitiesResult { get; set; }
}
Web Request
string url = "my url";
WebRequest request = WebRequest.Create(url);
// provide your ID And Password for proxy
request.Proxy.Credentials = new NetworkCredential(XXXX);
WebResponse ws = request.GetResponse();
DataContractJsonSerializer jsonSerializer =
new DataContractJsonSerializer(typeof(List<Model>));
List<Model> dataList =
(List<Model>)jsonSerializer.ReadObject(ws.GetResponseStream());
string result = "";
foreach (var data1 in dataList)
{
result = result + data1.lstCitiesResult.ToString();
}
TextBox1.Text = result.ToString();
My error: I got null in datalist...what i do?

Paypal PDT with C#

I'm trying to implement a shopping cart tracking system using pdt with C#.
the trouble i have is finding an example of the paypal succes postback, especially in case of multiple items !
Any help woul be appreciated (some code will be much better :D)!
Thanks
The code below parses PDT response:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Net;
using System.IO;
using System.Text;
namespace PayPal
{
public static class PaymentDataTransfer
{
private const string AppSetting_Identity = "PayPal.PaymentDataTransfer.Identity";
private const string AppSetting_ServiceUrl = "PayPal.PaymentDataTransfer.ServiceUrl";
public class TransactionStatus
{
public bool Success { get; set; }
public int ErrorCode { get; set; }
public NameValueCollection Parameters { get; set; }
public float PaymentGross { get; set; }
public string Currency { get; set; }
public string Invoice { get; set; }
public PayerInformation Payer { get; set; }
public CartItem[] CartItems;
}
public class PayerInformation
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class CartItem
{
public int Number { get; set; }
public string Name { get; set; }
public int Qunatity { get; set; }
public float GrossPrice { get; set; }
}
public static TransactionStatus GetTransactionStatus(string transactionToken)
{
return GetTransactionStatus(transactionToken, false);
}
public static TransactionStatus GetTransactionStatus(string transactionToken, bool sandbox)
{
#if ParsingTest
string response =
#"SUCCESS
mc_gross=1100.00
invoice=334354
protection_eligibility=Eligible
address_status=confirmed
item_number1=1
tax=0.00
item_number2=2
payer_id=DSFSDFSDFSDF
address_street=1+Main+St
payment_date=04%3A13%3A49+Oct+20%2C+2011+PDT
payment_status=Completed
charset=windows-1252
address_zip=95131
mc_shipping=0.00
mc_handling=0.00
first_name=Test
mc_fee=32.20
address_country_code=US
address_name=Test+User
custom=
payer_status=verified
business=yourbusiness%40business.com
address_country=United+States
num_cart_items=2
mc_handling1=0.00
mc_handling2=0.00
address_city=San+Jose
payer_email=payer_email%40business.com
mc_shipping1=0.00
mc_shipping2=0.00
txn_id=0SDFSDFSDFSDFD
payment_type=instant
last_name=User
address_state=CA
item_name1=First+test+item
receiver_email=yourbusiness%40business.com
item_name2=Second+test+item
payment_fee=32.20
quantity1=1
quantity2=1
receiver_id=SDFGDFGDFGDFDFG
txn_type=cart
mc_gross_1=1000.00
mc_currency=USD
mc_gross_2=100.00
residence_country=US
transaction_subject=Shopping+Cart
payment_gross=1100.00";
#else
string authToken = GetAppSetting(AppSetting_Identity, sandbox);
string serviceUrl = GetAppSetting(AppSetting_ServiceUrl, sandbox);
string query = string.Format("cmd=_notify-synch&tx={0}&at={1}", transactionToken, authToken);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUrl);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = query.Length;
using (var requestStreamWriter = new StreamWriter(request.GetRequestStream(), Encoding.ASCII))
{
requestStreamWriter.Write(query);
requestStreamWriter.Close();
}
string response;
// Do the request to PayPal and get the response
using(StreamReader stIn = new StreamReader(request.GetResponse().GetResponseStream()))
{
response = stIn.ReadToEnd();
stIn.Close();
}
#endif
string[] lines = response.Split(new []{"\n", "\r\n"}, StringSplitOptions.None); // splitting up by line breaks
var result = new TransactionStatus
{
Success = lines[0] == "SUCCESS",
Parameters = new NameValueCollection()
};
for(int i=1; i < lines.Length; i++)
{
string line = lines[i];
string[] keyValuePair = lines[i].Split(new [] {'='});
if(keyValuePair.Length == 2)
{
result.Parameters.Add(UrlDecode(keyValuePair[0]), UrlDecode(keyValuePair[1]));
}
else
{
const string errorCodePrefix = "Error:";
if(line.StartsWith(errorCodePrefix))
{
result.ErrorCode = Int32.Parse(line.Substring(errorCodePrefix.Length));
}
}
}
if(result.Success)
{
result.Invoice = result.Parameters["invoice"];
result.Payer = new PayerInformation
{
FirstName = result.Parameters["first_name"],
LastName = result.Parameters["last_name"],
Email = result.Parameters["payer_email"]
};
float paymentGross;
result.PaymentGross = float.TryParse(result.Parameters["mc_gross"],
NumberStyles.Float,
CultureInfo.InvariantCulture,
out paymentGross) ? paymentGross : 0.0f;
result.Currency = result.Parameters["mc_currency"];
int cartItemsNumber;
if (int.TryParse(result.Parameters["num_cart_items"], out cartItemsNumber) && cartItemsNumber > 0)
{
var cartItems = new List<CartItem>();
for(int i=1; i <= cartItemsNumber; i++)
{
cartItems.Add(new CartItem
{
Number = int.Parse(result.Parameters["item_number" + i], CultureInfo.InvariantCulture),
Name = result.Parameters["item_name" + i],
Qunatity = int.Parse(result.Parameters["quantity" + i], CultureInfo.InvariantCulture),
GrossPrice = float.Parse(result.Parameters["mc_gross_" + i], CultureInfo.InvariantCulture)
});
}
result.CartItems = cartItems.ToArray();
}
}
return result;
}
private static string UrlDecode(string encodedText)
{
return Uri.UnescapeDataString(encodedText.Replace("+", " "));
}
private static string GetAppSetting(string settingName, bool sandbox)
{
return ConfigurationManager.AppSettings[settingName + (sandbox ? "_sandbox" : string.Empty)];
}
}
}
The configuration part from web.config:
<appSettings>
.....
<!-- PayPal -->
<add key="PayPal.PaymentDataTransfer.Identity" value="....." />
<add key="PayPal.PaymentDataTransfer.Identity_sandbox" value="....." />
<add key="PayPal.PaymentDataTransfer.ServiceUrl" value="https://www.paypal.com/cgi-bin/webscr" />
<add key="PayPal.PaymentDataTransfer.ServiceUrl_sandbox" value="https://www.sandbox.paypal.com/cgi-bin/webscr" />
</appSettings>
I believe this book has some good examples.
http://www.amazon.com/Beginning-ASP-NET-E-Commerce-2005-ebook/dp/B001JEPVVE/ref=sr_1_2?ie=UTF8&s=digital-text&qid=1268249356&sr=8-2

Categories

Resources