15
\$\begingroup\$

This week's challenge is essentially about fetching Json data from the Web, and deserializing it into objects. I don't have much time to devote to this one, so what I have is very basic: it displays the names of all pokemons in a WPF ListView:

there's pikachu!

Here's the code for the window:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var service = new PokemonApp.Web.Pokedex();
        var pokemons = service.SelectAllPokemonReferences().OrderBy(pokemon => pokemon.Name);
        DataContext = new MainWindowViewModel { References = pokemons };
    }
}

As you've guessed, the interesting code is in the Pokedex class:

public class Pokedex
{
    public IEnumerable<ResourceReference> SelectAllPokemonReferences()
    {
        string jsonResult = GetJsonResult(PokeDexResultBase.BaseUrl + "pokedex/1/");

        dynamic pokemonRefs = JsonConvert.DeserializeObject(jsonResult);
        ICollection<ResourceReference> references = new List<ResourceReference>();

        foreach (var pokeRef in pokemonRefs.pokemon)
        {
            string uri = PokeDexResultBase.BaseUrl + pokeRef.resource_uri.ToString();
            references.Add(new ResourceReference
                            {
                                Name = pokeRef.name,
                                ResourceUri = new Uri(uri)
                            });
        }

        return references;
    }

    private static string GetJsonResult(string url)
    {
        string result;

        WebRequest request = HttpWebRequest.Create(url);
        request.ContentType = "application/json; charset=utf-8";

        try
        {
            using (var response = request.GetResponse())
            using (var stream = response.GetResponseStream())
            using (var reader = new StreamReader(stream))
            {
                result = reader.ReadToEnd();
            }
        }
        catch (Exception exception)
        {
            result = string.Empty;
            // throw?
        }

        return result;
    }
}

The ResourceReference class has nothing special:

public class ResourceReference
{
    public string Name { get; set; }
    public Uri ResourceUri { get; set; }
}

This quite basic code makes a foundation for the more complex objects:

public abstract class PokeDexResultBase : IPokeDexUri
{
    private readonly string _controller;
    protected PokeDexResultBase(string controller)
    {
        _controller = string.IsNullOrEmpty(controller) ? BaseUrl : controller;
    }

    public static string BaseUrl { get { return "http://pokeapi.co/api/v1/"; } }

    public int Id { get; set; }
    public Uri ResourceUri { get; set; }
    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }

    public virtual string Url { get { return BaseUrl + _controller + (Id == 0 ? string.Empty : Id.ToString()); } }
}

That base class is inherited by everything that has an Id in the API:

public class Pokemon : PokeDexResultBase
{
    public Pokemon() : base("pokemon/") { }

    public string Name { get; set; }
    public ICollection<ResourceReference> Abilities { get; set; }
    public ICollection<ResourceReference> EggGroups { get; set; }
    public ICollection<PokemonEvolution> Evolutions { get; set; }
    public ICollection<PokemonMove> Moves { get; set; }
    public ICollection<PokemonType> Types { get; set; }
    public int Attack { get; set; }
    public int CatchRate { get; set; }
    public int Defense { get; set; }
    public int EggCycles { get; set; }
    public string EvolutionYield { get; set; }
    public int ExperienceYield { get; set; }
    public string GrowthRate { get; set; }
    public int Happiness { get; set; }
    public string Height { get; set; }
    public int HitPoints { get; set; }
    public string MaleFemaleRatio { get; set; }
    public int SpecialAttack { get; set; }
    public int SpecialDefense { get; set; }
    public string Species { get; set; }
    public int Speed { get; set; }
    public int Total { get; set; }
    public string Weight { get; set; }
}

public class PokemonEvolution
{
    public int Level { get; set; }
    public string Method { get; set; }
    public Uri ResourceUri { get; set; }
    public string ToPokemonName { get; set; }
}

There are other classes involved, but there's nothing much to review about them, they're very similar to the Pokemon class.

As I extend my code I'll add more methods to the Pokedex class, which will use the GetJsonResult method.

Have I well analyzed the API - I mean, is this code a solid foundation for deserializing pokemons and eventually getting them to fight against each other? What could be done better? Any nitpicks?

\$\endgroup\$
3
\$\begingroup\$

If a class such as ResourceReference makes sense as far as wrapping the API is concerned, from a UI standpoint, it's like using POCO's in the UI layer: If the data came from a database through Entity Framework, this code would be displaying the entities. This is a basic implementation, but if the goal is to make it an extensible "skeleton" implementation, there should be a ViewModel class for the UI to display, independently of the WebAPI-provided objects; the ViewModel doesn't care about ResourceUri - this property isn't meant to be displayed, it's plumbing for querying the API, doesn't belong in the UI.

As far as the Pokedex service class goes, it looks like it could work, however it should be returning ViewModel objects for the UI to consume, and the static GetJsonResult method could be encapsulated in its own class, which would be an HTTP-based implementation of a helper object whose role is to fetch the data - there could be a SqlServer-based implementation that would do the same thing off a database, idea being to decouple the data from its data source... but it could be overkill.

Usage of Uri in the POCO classes adds unnecessary complexity: they could just as well be kept as normal strings, so this code:

string uri = PokeDexResultBase.BaseUrl + pokeRef.resource_uri.ToString();

Could then look like this, skipping the useless .ToString() call:

 string uri = PokeDexResultBase.BaseUrl + pokeRef.resource_uri;

The PokeDexResultBase.BaseUrl static string property would probably be better off as a static property of the Pokedex class, and then it would make PokeDexResultBase tightly coupled with Pokedex, which somehow makes sense.

Also, class PokeDexResultBase should be called PokedexResultBase, for consistency.

\$\endgroup\$
  • 1
    \$\begingroup\$ No need for Primitive Obsession. You can stick with Uri. You just need to use it consistently. new Uri(Uri, Uri) and WebRequest.Create(Uri) should do the trick. \$\endgroup\$ – abuzittin gillifirca Dec 21 '13 at 23:00
2
\$\begingroup\$

Actually, i ended up with this approach in deserializing

// Pocos for deserializing
public class PocoAnswer
{ 
    PocoItem item {get; set;}
}

public class PocoItem
{
    String name {get; set; }
    DateTime birthday {get; set; }
}

// Model for binding
public ItemModel
{  
    public readonly PocoItem item; // I wish this to stay private, but json serializer cant get it then
    public ItemModel(PocoItem _item)
    { 
         item = _item;
    }

    [JsonIgnore] // No need to store runtime generated data
    public FormattedInfoString // This is used for binding 
    {
        get 
        {
            return String.Format("My amazing custom string contains name {0} and birth {1}, item.Name, item.Birthday.ToString("yy-mm");
        }
    }
}

And when i'm getting answer from the server, i'm pushing it into binded item

var result = JsonConvert.DeserializeObject<PocoAnswer>(resultString);
BindableModel = new ItemModel(result.answer.item);

and then in xaml

<TextBlock Text={Binding BindableModel.FormattedInfoString} \>
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.