Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

05 March 2011

Easy access to WMAppManifest.xml App properties like version and title

Every Windows Phone 7 application must have an Application Manifest file called WMAppManifest.xml. In this you must state title, author, version and some other things. A quote from the link above:

“The primary purpose of this file is the following:

  • The Windows Phone Marketplace application submission process uses information from the manifest file. The manifest file supports the submission of applications to the Windows Phone Marketplace (including certification), device marketplace filtering, marketplace-to-device deployment, and device execution.
  • The information from the manifest file is used as the application metadata that will be stored in the application database.”

For some reason it’s not common knowledge that information in this file can accessed runtime as well. This can come in very handy, for example for showing a consistent app title and even more important, it’s version number. There are some examples of this to be found on the web but they are not very clear or pretty limited, so I cobbled together this little helper class to make retrieving attributes from the App tag a little easier:

using System.Collections.Generic;
using System.Xml.Linq;

namespace LocalJoost.Utilities
{
  /// <summary>
  /// A helper class to easily retrieve data from the WMAppManifest.xml
  /// App tag
  /// </summary>
  public class ManifestAppInfo
  {
    public ManifestAppInfo()
    {
    }

    static Dictionary<string, string> _properties;

    static Dictionary<string, string> Properties
    {
      get
      {
        if (null == _properties)
        {
          _properties = new Dictionary<string, string>();
          var appManifestXml = XDocument.Load("WMAppManifest.xml");
          using (var rdr = appManifestXml.CreateReader(ReaderOptions.None))
          {
            rdr.ReadToDescendant("App");
            if (!rdr.IsStartElement())
            {
              throw new System.FormatException(
                 "App tag not found in WMAppManifest.xml ");
            }
            rdr.MoveToFirstAttribute();
            while (rdr.MoveToNextAttribute())
            {
              _properties.Add(rdr.Name, rdr.Value);
            }
          }
        }
        return _properties;
      }
    }

    public string Version
    {
      get { return Properties["Version"]; }
    }

    public string ProductId
    {
      get { return Properties["ProductID"]; }
    }

    public string Title
    {
      get { return Properties["Title"]; }
    }

    public string TitleUc
    {
      get { return !string.IsNullOrEmpty(Title) ? 
                     Title.ToUpperInvariant() : null; }
    }

    public string Genre
    {
      get { return Properties["Genre"]; }
    }

    public string Description
    {
      get { return Properties["Description"]; }
    }

    public string Publisher
    {
      get { return Properties["Publisher"]; }
    }
  }
}

I got some feedback from Matthijs Hoekstra  on the Standard About Page that it actually showed the version number of the dll in which the AboutViewModel is stored, and not that of the app. This is because Assembly.GetExecutingAssembly() is used. There is also Assembly.GetCallingAssembly() but that shows the version of System.Windows.dll. What we need is Assembly.GetEntryAssembly() but that’s not available in the current version of the Windows Phone 7 framework. But we can now adapt the first part of the AboutViewModel as follows:

public class AboutViewModelBase : ViewModelBase
{
  private ManifestAppInfo _manifestAppInfo;
  public void LoadValuesFromResource<T>()
  {
    _manifestAppInfo = new ManifestAppInfo();
    var targetType = GetType();
    var sourceType = typeof(T);
    foreach (var targetProperty in targetType.GetProperties())
    {
      var sourceProperty = sourceType.GetProperty(targetProperty.Name, 
	     BindingFlags.Static | BindingFlags.Public);
      if (sourceProperty != null)
      {
        if (targetProperty.CanWrite)
        {
          targetProperty.SetValue(this, 
		    sourceProperty.GetValue(null, null), null);
        }
      }
    }
  }
  /// <Summary>A string value for the AppTitle</Summary>
  public string AppTitle
  {
    get
    {
      if (DesignerProperties.IsInDesignTool)
      {
        return "APPLICATION TITLE";
      }
      return _manifestAppInfo.Title;
      
    }
  }
  
  public string ApplicationVersion
  {
    get
    {
      if (DesignerProperties.IsInDesignTool)
        return "version x.x.x";

      var version = _manifestAppInfo.Version;
      return version.Substring(0, version.LastIndexOf("."));
    }
  }
}

And have the ViewModel show the version from the Application Manifest file regardless of actual assembly versions - as well as the title. You can even binding directly to its properties.

Now you might not want to use all of its properties directly in text – if your App Title shows up differently in different languages you still might want to get that from a resource file. That is why the LoadValuesFromResource method now checks if a property is writable. If you omit that check, it will try to write to every property for which a key with the same name can be found in the resource file – and if that’s now read-only, it will crash.

12 November 2010

Silverlight and Windows Phone 7 do not like DTDs

My current Windows Phone 7 project requires the possibility of reading configurations from various servers. For the GIS lovers among my audience: I am trying to read a GetCapabilities document from a WMS server. I was reading this one and this one, and the first one succeeded, while the second one failed. It became even more odd when I tried to write unit tests running from the full framework – then both links processed flawlessly. The error was “NotSupportedException” on XDocument.Load().

The second links contains a DTD from ye goode olde days and it turns out Silverlight and Windows Phone 7 by default are not very fond of DTD’s in documents. After some searching around I found the DtdProcessing Enumeration and I changed this code

var doc = XDocument.Load(stream)

into

using (var reader = XmlReader.Create(stream, 
  new XmlReaderSettings {DtdProcessing = DtdProcessing.Ignore}))
{
  var doc = XDocument.Load(reader)
}

And indeed, problem solved

15 August 2009

Using extension methods to serialize objects to XML and compress the result - and deserialize again

Elaborating upon the string extension methods I created for compressing and decompressing strings to and from a byte array, it turned out fairly easy to create another set of compress / decompress methods to serialize any type of object to and from a byte array containing compressed XML. I once again took the StringZipExtensions class and added the following methods to compress any old object to a byte array:
/// <summary>
/// XmlSerializes the object to a compressed byte array
/// using the specified encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress,
  Encoding encoding)
{
  var xmlSerializer = new XmlSerializer(objectToCompress.GetType());
  using (var stringWriter = new StringWriter())
  {
    xmlSerializer.Serialize(stringWriter, objectToCompress);
    return stringWriter.ToString().Compress(encoding);
  }
}

/// <summary>
/// XmlSerializes the object to a compressed byte array using default
/// UTF8 encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress)
{
  return Compress(objectToCompress, new UTF8Encoding());
}
Here, once again, an overload using a default UTF8 encoding and a method which uses your own encoding to do the heavy lifting. Then, the methods for decompressing, which - in all modesty - are a rather neat usage of generics, I think:
/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using the specified encoding
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="compressedObject">The compressed string.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject,
  Encoding encoding)
{
  var xmlSer = new XmlSerializer(typeof(T));
  return (T)xmlSer.Deserialize(new StringReader(
    compressedObject.DecompressToString(encoding)));
}

/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using default UTF8 encoding
/// </summary>
/// <param name="compressedObject">The compressed string.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject )
{
  return DecompressToObject<T>(compressedObject, new UTF8Encoding());
}
Then you can take something like this hugely complex ;-) object Person
public class Person
{
  public int Id { get; set; }
  public string Name { get; set; }
  public DateTime Birthday { get; set; }

  public override bool Equals(object obj)
  {
    var toCompare = obj as Person;
    if( toCompare != null )
    {
      return
        Name.Equals(toCompare.Name) &&
        Id.Equals(toCompare.Id) &&
        Birthday.Equals(toCompare.Birthday);

    }
    return base.Equals(obj);
  }
}
and serialize/compress and decompress/deserialize it like this:
[Test]
public void CompressObjectTest()
{
  var baseLineObject = new Person
  {
  Id = 99, 
  Name = "Tom", 
  Birthday = new DateTime(1969, 06, 03)
  };
  var compressed = baseLineObject.Compress();
  var testObject = compressed.DecompressToObject<Person>();
  Assert.AreEqual(testObject, baseLineObject);
}
Code downloadable here

24 July 2009

Storing objects as compressed messages in the Windows Azure Queue

For my current private R&D project I wanted to store 'task set' objects (in code samples below as shown type "Request") on the Windows Azure Queue. To prevent serialization issues I opted for XML serialization, like this:
private void Enqueue(Request tr)
{
  var queueStorage = QueueStorage.Create(
  StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());
  var queue = queueStorage.GetQueue("MyQueue");
  if (!queue.DoesQueueExist())
  {
    queue.CreateQueue();
  }

  var xmlSerializer = new XmlSerializer(tr.GetType());
  using (var stringWriter = new StringWriter())
  {
    xmlSerializer.Serialize(stringWriter, tr);
    queue.PutMessage(new Message(stringWriter.ToString()));
  }
}
I would let the worker role deserialize the Request object and then execute it. It annoyed me to no end to learn that the Azure Queue limits message sizes to 8192 bytes. I could have redesigned my task sets to smaller units, but that would hurt the efficiency of the process I had in mind. Based upon the StringZipExtensions class I blogged about that can serialize and deserialize any old object to and from compressed XML (which you can download here) I created the following extension methods, which enable you to store objects as a GZip compressed set of bytes on the Azure queue and retrieve them again:
using LocalJoost.Utilities.Compression;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace LocalJoost.Utilities.Azure
{
  public static class MessageQueueExtensions
  {
    /// <summary>
    /// Decompresses the specified queue message.
    /// </summary>
    /// <param name="message">The message.</param>
    /// <returns></returns>
    public static string Decompress(this Message message)
    {
      return message != null ?
       message.ContentAsBytes().DecompressToString() : null;
    }

    /// <summary>
    /// Decompresses the specified queue message.
    /// to an object
    /// </summary>
    /// <param name="message">The message.</param>
    /// <returns></returns>
    public static T Decompress <T>(this Message message) where T:class
    {
      return message != null ?
       message.ContentAsBytes().DecompressToObject<T>() : null;
    }
  }
}
Adhering to my first code sample, you can now simply put an object to the queue like this
private void Enqueue(Request tr)
{
  var queueStorage = QueueStorage.Create(
    StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());
  var queue = queueStorage.GetQueue("MyQueue");
  if (!queue.DoesQueueExist())
  {
    queue.CreateQueue();
  }

  queue.PutCompressedObject(tr);
}
and let the worker role use the Decompress extension method on the Message itself:
var queueStorage =
  QueueStorage.Create(
  StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());
var queue = queueStorage.GetQueue("MyQueue");
if (queue.DoesQueueExist())
{
  var message = queue.GetMessage(600);
  if (message != null)
  {
    var request = message.Decompress<Request>();
    request.Execute();
    queue.DeleteMessage(message);
  }
}
And there you go. It is as simple as that. Although the Window Azure Queue message queue size still is limited to 8192, the amount of data that fits in that space is increased dramatically.