Business Logic Toolkit for .NET
www.bltoolkit.net
Welcome Guest, you are in: Login
|  Home   |  Download   |  Documentation   |  Discussions   |  Issues   |  License   |
RSS RSS

Navigation




Search the wiki
»

PoweredBy
MapToJson.cs

using System;
using System.Globalization;
using System.Text;
using System.Xml;

using NUnit.Framework;

using BLToolkit.Mapping; using BLToolkit.Reflection;

namespace HowTo.Mapping { public class JsonMapper : MapDataDestinationBase, IMapDataDestinationList, ISupportMapping { private static readonly long InitialJavaScriptDateTicks = new DateTime(1970, 1, 1).Ticks;

private string[] _fieldNames; private readonly StringBuilder _sb; private MappingSchema _mappingSchema; private bool _scalar; private bool _first; private bool _firstElement; private int _indent;

public JsonMapper() : this(new StringBuilder(), 0) { }

public JsonMapper(StringBuilder sb) : this(sb, 0) { }

public JsonMapper(StringBuilder sb, int indent) { _sb = sb; _indent = indent; }

public override Type GetFieldType(int index) { // Same as typeof(object) // return null; }

public override int GetOrdinal(string name) { return Array.IndexOf(_fieldNames, name); }

public override void SetValue(object o, int index, object value) { SetValue(o, _fieldNames[index], value); }

public override void SetValue(object o, string name, object value) { if (!_scalar) { // Do not Json null values until it's an array // if (value == null || (value is XmlNode && IsEmptyNode((XmlNode)value))) return;

if (_first) _first = false; else _sb .Append(',') .AppendLine() ;

for (int i = 0; i < _indent; ++i) _sb.Append(' ');

_sb .Append('"') .Append(name) .Append("\":") ; }

if (value == null) _sb.Append("null"); else { switch (Type.GetTypeCode(value.GetType())) { case TypeCode.Empty: case TypeCode.DBNull: _sb.Append("null"); break; case TypeCode.Boolean: _sb.Append((bool)value? "true": "false"); break; case TypeCode.Char: _sb .Append('\'') .Append((char)value) .Append('\'') ; break; case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: _sb.Append(((IFormattable)value).ToString(null, CultureInfo.InvariantCulture)); break; case TypeCode.DateTime: _sb .Append("new Date(") .Append((((DateTime)value).Ticks - InitialJavaScriptDateTicks)/10000) .Append(")"); break; case TypeCode.String: _sb .Append('"') .Append(encode((string)value)) .Append('"') ; break; default: if (value is XmlNode) { if (IsEmptyNode((XmlNode) value)) _sb.Append("null"); else WriteXmlJson((XmlNode)value); } else { JsonMapper inner = new JsonMapper(_sb, _indent + 1);

if (value.GetType().IsArray) _mappingSchema.MapSourceListToDestinationList( _mappingSchema.GetDataSourceList(value), inner); else _mappingSchema.MapSourceToDestination( _mappingSchema.GetDataSource(value), value, inner, inner); } break; } } }

private static string encode(string value) { return value.Replace("\r\n", "\\r") .Replace("\n\r", "\\r") .Replace("\n", "\\r") .Replace("\r", "\\r") .Replace("\"","\\\""); }

private void WriteXmlJson(XmlNode node) { XmlNode textNode = GetTextNode(node); if (textNode != null) { _sb .Append("
\"") .Append(encode(textNode.Value)) .Append('\"') ; } else {

bool first = true;

_sb.Append('{');

if (node.Attributes != null) { foreach (XmlAttribute attr in node.Attributes) { if (first) first = false; else _sb.Append(',');

_sb .Append("\"@") .Append(attr.Name) .Append("\":\"") .Append(encode(attr.Value)) .Append('\"') ; } }

foreach (XmlNode child in node.ChildNodes) { if (IsWhitespace(child) || IsEmptyNode(child)) continue;

if (first) first = false; else _sb.Append(',');

if (child is XmlText) _sb .Append("\"#text\":\"") .Append(encode(child.Value)) .Append('\"') ; else if (child is XmlElement) { _sb .Append('"') .Append(child.Name) .Append("\":") ; WriteXmlJson(child); } else System.Diagnostics.Debug.Fail("Unexpected node type " + child.GetType().FullName); } _sb.Append('}'); } }

private static bool IsWhitespace(XmlNode node) { switch (node.NodeType) { case XmlNodeType.Comment: case XmlNodeType.Whitespace: case XmlNodeType.SignificantWhitespace: return true; } return false; }

private static bool IsEmptyNode(XmlNode node) { if (node.Attributes != null && node.Attributes.Count > 0) return false;

if (node.HasChildNodes) foreach (XmlNode childNode in node.ChildNodes) { if (IsWhitespace(childNode) || IsEmptyNode(childNode)) continue;

// Not a whitespace, nor inner empty node. // return false; }

return node.Value == null; }

private static XmlNode GetTextNode(XmlNode node) { if (node.Attributes != null && node.Attributes.Count > 0) return null;

XmlNode textNode = null;

foreach (XmlNode childNode in node.ChildNodes) { // Ignore all whitespace. // if (IsWhitespace(childNode)) continue;

if (childNode is XmlText) { // More then one text node. // if (textNode != null) return null;

// First text node. // textNode = childNode; } else // Not a text node - break; // return null; }

return textNode; }

#region ISupportMapping Members

void ISupportMapping.BeginMapping(InitContext initContext) { _first = true; _mappingSchema = initContext.MappingSchema; _fieldNames = new string[initContext.DataSource.Count];

for (int i = 0; i < _fieldNames.Length; ++i) _fieldNames[i] = initContext.DataSource.GetName(i);

_scalar = _fieldNames.Length == 1 && string.IsNullOrEmpty(_fieldNames[0]);

if (_scalar) return;

if (_fieldNames.Length <= 1) { // Reset the indent since output is a single line. // _indent = 0; _sb.Append('{'); } else { if (_indent > 0) _sb.AppendLine();

for (int i = 0; i < _indent; ++i) _sb.Append(' ');

_sb .Append('{') .AppendLine() ; } }

void ISupportMapping.EndMapping(InitContext initContext) { if (_scalar) return;

if (_fieldNames.Length > 1) _sb.AppendLine();

for (int i = 0; i < _indent; ++i) _sb.Append(' '); _sb.Append('}'); }

#endregion

#region
IMapDataDestinationList Members

void IMapDataDestinationList.InitMapping(InitContext initContext) { _firstElement = true; _sb.Append('['); }

IMapDataDestination IMapDataDestinationList.GetDataDestination(InitContext initContext) { return this; }

object IMapDataDestinationList.GetNextObject(InitContext initContext) { if (_firstElement) _firstElement = false; else _sb.Append(',');

return this; }

void IMapDataDestinationList.EndMapping(InitContext initContext) { _sb.Append(']'); }

#endregion

public override string
ToString() { return _sb.ToString(); } }

[TestFixture] public class MapToJson { public class Inner { public string Name = "inner \"object \n name"; }

public class Inner2 { public string Name; public int Value; }

public class SourceObject { public string Foo = "Foo"; public double Bar = 1.23; public DateTime Baz = DateTime.Today; [MapIgnore(false)] public Inner Inner = new Inner(); [MapIgnore(false)] public Inner2 Inner2 = new Inner2(); public string[] StrArray = {"One", "Two", "Three"}; }

[Test] public void Test() { JsonMapper jm = new JsonMapper(new StringBuilder(256));

Map.MapSourceToDestination(Map.GetObjectMapper(typeof(SourceObject)), new SourceObject(), jm, jm); Console.Write(jm.ToString());

// Expected output: // // { // "Foo":"Foo", // "Bar":1.23, // "Baz":new Date(11823840000000000), // "Inner":{ "Name":"inner \"object \r name"}, // "Inner2": // { // "Name":null, // "Value":0 // }, // "StrArray":["One","Two","Three"] // } } } }
© 2010 www.bltoolkit.net