Some JSON is only used in specific places. For these occurrences I use extension methods / meta methods to easily convert my classes into the required JSON without the need for a specific mapping/dto class. To do this I declare a Markup interface (called Mappable) on which the method can be invoked.
I put the following interface in src/groovy:
package com.example.json
/**
* Markup interface so we can add extension methods to convert objects to maps.
* @see MetaMethods.groovy
*/
interface Mappable {} |
package com.example.json
/**
* Markup interface so we can add extension methods to convert objects to maps.
* @see MetaMethods.groovy
*/
interface Mappable {}
I then add the following to my MetaMethods class in src/groovy:
package com.example.json;
class MetaMethods {
static void register() {
Mappable.metaClass.toMap { whitelist, blacklist ->
/* metaClass, class */
def map = [:];
/* id seems to be a special case */
if (("id" in whitelist) || (!"id" in blacklist)) {
map["id"] = delegate["id"];
}
if (whitelist) {
whitelist.each {
map[it] = delegate[it]
}
} else if (blacklist) {
delegate.properties.each { prop, val ->
if (!(prop in blacklist)) {
map[prop] = val;
}
}
}
return map;
}
}
} |
package com.example.json;
class MetaMethods {
static void register() {
Mappable.metaClass.toMap { whitelist, blacklist ->
/* metaClass, class */
def map = [:];
/* id seems to be a special case */
if (("id" in whitelist) || (!"id" in blacklist)) {
map["id"] = delegate["id"];
}
if (whitelist) {
whitelist.each {
map[it] = delegate[it]
}
} else if (blacklist) {
delegate.properties.each { prop, val ->
if (!(prop in blacklist)) {
map[prop] = val;
}
}
}
return map;
}
}
}
I register the metamethods in my Bootstrap.groovy
class BootStrap {
def init = { servletContext ->
/* Register our own Meta Methods/Extension Methods */
MetaMethods.register();
}
} |
class BootStrap {
def init = { servletContext ->
/* Register our own Meta Methods/Extension Methods */
MetaMethods.register();
}
}
Now we can create easy JSON dto’s based on our domain (don’t forget to add the Mappable interface to your domain classes).
Usage example:
def jsonMap = myDomainClass.toMap(["id", "propertyX", "propertyZ"], []); // 1st level properties
jsonMap["customer"] = myDomainClass.customer ? myDomainClass.customer.toMap(["id", "fullName", "firstName", "insertion", "lastName"], []) : null; // 2nd level properties
render jsonMap as JSON; // or as XML |
def jsonMap = myDomainClass.toMap(["id", "propertyX", "propertyZ"], []); // 1st level properties
jsonMap["customer"] = myDomainClass.customer ? myDomainClass.customer.toMap(["id", "fullName", "firstName", "insertion", "lastName"], []) : null; // 2nd level properties
render jsonMap as JSON; // or as XML
Happy Grailing!
Made up this extension method for XDocuments to be able to return an Expando object on which the properties of the XML can be navigated. This is still a little bit rough. I found out there is a ElasticObject that probably works a lot better. But this is just a little ‘proof of concept’.
Note that attributes in the XML are not bound in the Expando object.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using System.Xml.Linq;
namespace Famvdploeg.Com
{
public static class XDocumentExtensions
{
public static dynamic ToExpandoObject(this XDocument document)
{
return ParseNode(document.Root);
}
private static dynamic ParseNode(XElement item)
{
dynamic expando = new ExpandoObject();
var props = expando as IDictionary<string, object>;
/* Properties of current node. Single and Lists are created here dynamic */
IEnumerable<IGrouping<string, XElement>> groupedProperties = item.Elements().Where(element => !element.HasElements).GroupBy(x => x.Name.ToString());
foreach (IGrouping<string, XElement> propertyGroup in groupedProperties)
{
int nrElements = propertyGroup.Count();
if (nrElements == 1)
{
props[propertyGroup.Key.ToValidPropertyName()] = propertyGroup.First().Value;
}
else
{
var multipleValues = new List<string>();
foreach (XElement element in propertyGroup)
{
multipleValues.Add(element.Value);
}
props[propertyGroup.Key.ToValidPropertyName()] = multipleValues;
}
}
/* Children of current node. Single and Lists are created here dynamic */
IEnumerable<IGrouping<string, XElement>> groupedElements = item.Elements().Where(element => element.HasElements).GroupBy(x => x.Name.ToString());
foreach (IGrouping<string, XElement> elementGroup in groupedElements)
{
int nrElements = elementGroup.Count();
if (nrElements == 1)
{
props[elementGroup.Key.ToValidPropertyName()] = ParseNode(elementGroup.First());
}
else
{
var multipleElements = new List<dynamic>();
foreach (XElement element in elementGroup)
{
multipleElements.Add(ParseNode(element));
}
props[elementGroup.Key.ToValidPropertyName()] = multipleElements;
}
}
return expando;
}
}
} |
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using System.Xml.Linq;
namespace Famvdploeg.Com
{
public static class XDocumentExtensions
{
public static dynamic ToExpandoObject(this XDocument document)
{
return ParseNode(document.Root);
}
private static dynamic ParseNode(XElement item)
{
dynamic expando = new ExpandoObject();
var props = expando as IDictionary<string, object>;
/* Properties of current node. Single and Lists are created here dynamic */
IEnumerable<IGrouping<string, XElement>> groupedProperties = item.Elements().Where(element => !element.HasElements).GroupBy(x => x.Name.ToString());
foreach (IGrouping<string, XElement> propertyGroup in groupedProperties)
{
int nrElements = propertyGroup.Count();
if (nrElements == 1)
{
props[propertyGroup.Key.ToValidPropertyName()] = propertyGroup.First().Value;
}
else
{
var multipleValues = new List<string>();
foreach (XElement element in propertyGroup)
{
multipleValues.Add(element.Value);
}
props[propertyGroup.Key.ToValidPropertyName()] = multipleValues;
}
}
/* Children of current node. Single and Lists are created here dynamic */
IEnumerable<IGrouping<string, XElement>> groupedElements = item.Elements().Where(element => element.HasElements).GroupBy(x => x.Name.ToString());
foreach (IGrouping<string, XElement> elementGroup in groupedElements)
{
int nrElements = elementGroup.Count();
if (nrElements == 1)
{
props[elementGroup.Key.ToValidPropertyName()] = ParseNode(elementGroup.First());
}
else
{
var multipleElements = new List<dynamic>();
foreach (XElement element in elementGroup)
{
multipleElements.Add(ParseNode(element));
}
props[elementGroup.Key.ToValidPropertyName()] = multipleElements;
}
}
return expando;
}
}
}
1. Use xsd.exe to extract an xsd file from an xml feed.
Download a private xml feed for example from “http://api.twitter.com/1/statuses/user_timeline.xml” and store it to local disk. Use xsd.exe to generate the xsd.
2. Generate the classes from the xsd file you just generated.
3. Add the generated classes to your C# project.
4. Time for some fetching here is a short example:
// encode the username/password
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://api.twitter.com/1/statuses/user_timeline.xml");
// set the method to GET
request.Method = "GET";
request.ServicePoint.Expect100Continue = false;
// set the authorisation levels
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType = "application/x-www-form-urlencoded";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
XmlSerializer serializer = new XmlSerializer(typeof(statuses));
statuses s = serializer.Deserialize(response.GetResponseStream()) as statuses;
listBox1.Items.Clear();
foreach (statusesStatus status in s.status)
{
listBox1.Items.Add(status.text);
}
} |
// encode the username/password
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://api.twitter.com/1/statuses/user_timeline.xml");
// set the method to GET
request.Method = "GET";
request.ServicePoint.Expect100Continue = false;
// set the authorisation levels
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType = "application/x-www-form-urlencoded";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
XmlSerializer serializer = new XmlSerializer(typeof(statuses));
statuses s = serializer.Deserialize(response.GetResponseStream()) as statuses;
listBox1.Items.Clear();
foreach (statusesStatus status in s.status)
{
listBox1.Items.Add(status.text);
}
}