5
\$\begingroup\$

This code is still very naive. I am trying to convert an existing XSD to JSONSchema. Firstly, pardon the variable names (I know some are really just stupid but I will fix them once I get all the functionality to work.)

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using Newtonsoft.Json;
namespace XSDToJson
    {
    public class ReadXsd
        {
        private static string GetTargetSchema(string filepath)
        {
            var doc = new XmlDocument();
            doc.Load(filepath);
            return doc.OfType<XmlElement>().First().Attributes["targetNamespace"].Value;
        }

        public static void ValidationCallback(object sender, ValidationEventArgs args)
        {
            switch (args.Severity)
            {
                case XmlSeverityType.Warning:
                    Console.Write("WARNING: ");
                    break;
                case XmlSeverityType.Error:
                    Console.Write("ERROR: ");
                    break;
            }

            Console.WriteLine(args.Message);
        }

        private static Dictionary<String, Object> HandleSimpleTypeRestriction(XmlSchemaSimpleTypeRestriction restriction)
        {
            var res = new Dictionary<String, Object>();
            foreach (var t in restriction.Facets.OfType<XmlSchemaEnumerationFacet>())
            {
                var d = new Dictionary<String, Object>{{"fixed", t.IsFixed}};
                var annotation = HandleAnnotation(t.Annotation);
                if (annotation != "")
                    d.Add("description", annotation);
                res.Add(t.Value,d);
            }

            foreach (var t in restriction.Facets.OfType<XmlSchemaMaxExclusiveFacet>())
            {
                var d = new Dictionary<String, Object> { { "fixed", t.IsFixed } };
                var annotation = HandleAnnotation(t.Annotation);
                if (annotation != "")
                    d.Add("description", annotation);
                res.Add(t.Value, d);
            }

            foreach (var t in restriction.Facets.OfType<XmlSchemaMinExclusiveFacet>())
            {
                var d = new Dictionary<String, Object> { { "fixed", t.IsFixed } };
                var annotation = HandleAnnotation(t.Annotation);
                if (annotation != "")
                    d.Add("description", annotation);
                res.Add(t.Value, d);
            }
            return res;
        }

        private static dynamic HandleSimpleType(XmlSchemaSimpleType simpleType)
        {
            var temp = HandleAnnotation(simpleType.Annotation);

            var simple = new Dictionary<String, Object>
                             {
                                 {"type", simpleType.TypeCode.ToString()},
                                 {"description", temp},
                                 {"final", simpleType.Final.ToString()},
                                 {"datatype", simpleType.Datatype.TypeCode.ToString()},
                                 {"derivedby", simpleType.DerivedBy.ToString()}
                             };

            if (simpleType.Content != null)
            {
                if (simpleType.Content is XmlSchemaSimpleTypeRestriction)
                {
                    var res = HandleSimpleTypeRestriction(simpleType.Content as XmlSchemaSimpleTypeRestriction);
                    if (res.Count > 0)
                        simple.Add("enum", res);
                }
                else if (simpleType.Content is XmlSchemaSimpleTypeList)
                {
                    var list = simpleType.Content as XmlSchemaSimpleTypeList;
                    simple["type"] = "array";
                    simple.Add("items", new Dictionary<String,Object>{{"type",list.ItemTypeName.Name}});
                }
            }
            return simple ;
            }

        private static dynamic HandleComplexType(XmlSchemaComplexType complexType)
        {
            dynamic properties = new ExpandoObject();

            properties.type = complexType.BaseXmlSchemaType.QualifiedName.Name;

            properties.extends = new ExpandoObject();

            ((IDictionary<String, Object>) properties.extends).Add("$ref",
                                                                   complexType.BaseXmlSchemaType.QualifiedName.Namespace +
                                                                   "/" +
                                                                   complexType.BaseXmlSchemaType.QualifiedName.Name +
                                                                   "#");
            properties.description = HandleAnnotation(complexType.Annotation);

            if (complexType.AttributeUses.Count > 0)
            {
                var enumerator = complexType.AttributeUses.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    var attribute = (XmlSchemaAttribute) enumerator.Value;
                    if (attribute == null) continue;

                    if (((IDictionary<String, Object>) properties).ContainsKey(attribute.QualifiedName.Name))
                        ((IDictionary<String, Object>) properties).Add(attribute.QualifiedName.Name + "Hello",
                                                                       HandleAttribute(attribute));
                    else
                        ((IDictionary<String, Object>) properties).Add(attribute.QualifiedName.Name,
                                                                       HandleAttribute(attribute));
                }
            }

            if (complexType.AnyAttribute != null)
                    ((IDictionary<String, Object>)properties).Add("additionalItems",new Dictionary<String,Object>{{"$ref",complexType.AnyAttribute.Namespace}});

            var sequence = complexType.ContentTypeParticle as XmlSchemaSequence;

            if (sequence != null)
            {
                foreach (XmlSchemaParticle childParticle in sequence.Items)
                {
                    dynamic d2 = new ExpandoObject();
                    var element = (childParticle as XmlSchemaElement);
                    if (element == null) continue;

                    d2.type = element.QualifiedName.Name;

                    d2.extends = new ExpandoObject();
                    ((IDictionary<String, Object>) d2.extends).Add("$ref",
                                                                   element.QualifiedName.Namespace + "/" +
                                                                   element.QualifiedName.Name + "#");

                    if (element.DefaultValue != null)
                        ((IDictionary<String, Object>) d2).Add("default", element.DefaultValue);
                    if (element.IsAbstract)
                        ((IDictionary<String, Object>) d2).Add("abstract", element.IsAbstract);

                    d2.minItems = childParticle.MinOccursString;
                    d2.maxItems = childParticle.MaxOccursString;

                    if (((IDictionary<String, Object>) properties).ContainsKey(element.QualifiedName.Name))
                        ((IDictionary<String, Object>) properties).Add(element.QualifiedName.Name + "Hello", d2);
                    else
                        ((IDictionary<String, Object>) properties).Add(element.QualifiedName.Name, d2);
                }
            }

            var choice = complexType.ContentTypeParticle as XmlSchemaChoice;

            if (choice != null)
            {
                if (((IDictionary<String, Object>)properties).ContainsKey("enum"))
                    ((IDictionary<String, Object>)properties).Add("enum"+ "Hello", HandleChoice(choice));
                else
                    ((IDictionary<String, Object>)properties).Add("enum", HandleChoice(choice));
            }

            return properties;
        }

        private static dynamic HandleChoice(XmlSchemaChoice choice)
        {
            dynamic d2 = new ExpandoObject();
            d2.description = HandleAnnotation(choice.Annotation);
            foreach (var item in choice.Items)
                ((IDictionary<String, Object>)d2).Add(((XmlSchemaElement)item).QualifiedName.Name, FixElement(item as XmlSchemaElement));
            d2.minItems = choice.MinOccursString;
            d2.maxitems = choice.MaxOccursString;
            return d2;
        }

        private static dynamic HandleXmlSchemaParticle(XmlSchemaParticle childParticle)
        {

                    dynamic d2 = new ExpandoObject();
                    var element = (childParticle as XmlSchemaElement);

                    if (element == null) return null;

                    d2.type = element.QualifiedName.Name;

                    d2.extends = new ExpandoObject();
                    ((IDictionary<String, Object>)d2.extends).Add("$ref", element.QualifiedName.Namespace + "/" + element.QualifiedName.Name + "#");

                    if (element.IsAbstract)
                        ((IDictionary<String, Object>)d2).Add("abstract", element.IsAbstract);

                    if (element.DefaultValue != null)
                        ((IDictionary<String, Object>)d2).Add("default", element.DefaultValue);

                    d2.minItems = childParticle.MinOccursString;
                    d2.maxItems = childParticle.MaxOccursString;
                    d2.description = HandleAnnotation(element.Annotation);

                    if (element.ElementSchemaType is XmlSchemaComplexType)
                    {
                        var c = element.ElementSchemaType as XmlSchemaComplexType;
                        var complex = HandleComplexType(c);
                        ((IDictionary<String, Object>)d2).Add(c.BaseXmlSchemaType.QualifiedName.Name, complex);
                    }
                    else if (element.ElementSchemaType is XmlSchemaSimpleType)
                    {
                        var simple = HandleSimpleType(element.ElementSchemaType as XmlSchemaSimpleType);
                        ((IDictionary<String, Object>)d2).Add(element.ElementSchemaType.QualifiedName.Name, simple);
                    }

                    return d2;
        }

        private static dynamic HandleComplexTypeFromElement(XmlSchemaComplexType complexType)
        {
           dynamic d = new ExpandoObject();
                if (complexType.AttributeUses.Count > 0){
                    var enumerator = complexType.AttributeUses.GetEnumerator();
                    while (enumerator.MoveNext()){
                      var attribute = (XmlSchemaAttribute)enumerator.Value;
                      var d2 =   HandleAttribute(attribute);
                      ((IDictionary<string, Object>)d).Add(attribute.QualifiedName.Name, d2);
                    }
                }

                var sequence = complexType.ContentTypeParticle as XmlSchemaSequence;
                if (sequence != null){
                        foreach (XmlSchemaParticle childParticle in sequence.Items){
                            var element = (childParticle as XmlSchemaElement);
                            if (element == null) continue;
                            if (((IDictionary<string, Object>)d).ContainsKey(element.QualifiedName.Name))
                                ((IDictionary<string, Object>)d).Add(element.QualifiedName.Name + "Hello", HandleXmlSchemaParticle(childParticle));
                            else
                                ((IDictionary<string, Object>)d).Add(element.QualifiedName.Name, HandleXmlSchemaParticle(childParticle));
                        }
                 }
                return d;
          }

        private static dynamic IterateOverElement(XmlSchemaElement element)
        {
            var complexType = element.ElementSchemaType as XmlSchemaComplexType;
            if (complexType == null)
            {
                var simpleType = element.ElementSchemaType as XmlSchemaSimpleType;
                return simpleType == null ? new ExpandoObject() : HandleSimpleType(simpleType);
            }
            return HandleComplexTypeFromElement(complexType);
        }

        private static string GetSchemaDescription(XmlSchemaAnnotation annotation)
        {
            //Get the count of the elements in Items , then chekc if it appinfo or documentation and then act accordingly 
            if (annotation == null || annotation.Items == null) return "N/A";
            var count = annotation.Items.Count;
            for (var i = 0; i < count; i++)
            {
                if (!(annotation.Items[i] is XmlSchemaDocumentation)) continue;
                var d = annotation.Items[i] as XmlSchemaDocumentation;
                if (d == null) continue;
              //  Console.WriteLine("Annotation" + d.Markup[i].InnerText);
                var temp = "";
                for (var j = 0; j < d.Markup.Count(); j ++ )
                    temp += "\n"+ d.Markup[j].InnerText;
                return temp;
            }
            return "N/A";
        }

        private static string HandleAnnotation(XmlSchemaAnnotation annotation)
        {
            //Get the count of the elements in Items , then chekc if it appinfo or documentation and then act accordingly 
            if (annotation == null || annotation.Items == null) return "";
            var count = annotation.Items.Count;
            for (var i = 0; i < count; i++)
            {
                if (annotation.Items[i] is XmlSchemaDocumentation)
                {
                    var d = annotation.Items[i] as XmlSchemaDocumentation;
                    if (d != null){
                        return d.Markup[i].InnerText;
                    }
                }
                else if (annotation.Items[i] is XmlSchemaAppInfo)
                {
                    var appinfo = annotation.Items[i] as XmlSchemaAppInfo;
                    if (appinfo != null){
                        if (appinfo.Markup == null) return "";
                        var markupcount = appinfo.Markup.Count();
                        for (var k = 0; k < markupcount; k++){ 
                        var ns = appinfo.Markup[k].NamespaceURI + "/" + appinfo.Markup[k].LocalName;
                        var attributeCount = appinfo.Markup[0].Attributes.Count;
                        for (var j = 0; j < attributeCount; j++){
                            Console.WriteLine(appinfo.Markup[k].Attributes[j].Value);
                        }
                        }
                    }
                }
            }
            return "";
        }

        private static dynamic HandleAttributeGroup(XmlSchemaAttributeGroup attributegroup)
        {
            dynamic d = new ExpandoObject();
            d.description = HandleAnnotation(attributegroup.Annotation);
            foreach (var attr in attributegroup.Attributes.OfType<XmlSchemaAttribute>())
            {
                var a = HandleAttribute(attr);
                ((IDictionary<String, Object>) d).Add(attr.QualifiedName.Name,a);
            }
            foreach (var attr in attributegroup.Attributes.OfType<XmlSchemaAttributeGroupRef>())
            {
                d.extends = new ExpandoObject();
                ((IDictionary<String, Object>)d.extends).Add("$ref",attr.RefName.Namespace+"/"+attr.RefName.Name+"#");
            }
            return d; 
        }

        private static dynamic HandleAttribute(XmlSchemaAttribute attribute)
        {
            dynamic attr = new ExpandoObject();

            attr.type = attribute.AttributeSchemaType.QualifiedName.Name;

            attr.extends = new ExpandoObject();
            ((IDictionary<String, Object>)attr.extends).Add("$ref", attribute.AttributeSchemaType.QualifiedName.Namespace + "/" + attribute.AttributeSchemaType.QualifiedName.Name + "#");

            if (HandleAnnotation(attribute.Annotation) != "")
                attr.description = HandleAnnotation(attribute.Annotation) ;

            if(attribute.DefaultValue != null)
                ((IDictionary<String, Object>)attr).Add("default", attribute.DefaultValue);

            if (attribute.Use == XmlSchemaUse.Required)
                attr.required = true;

            return attr;
        }

        private static dynamic FixElement(XmlSchemaElement element)
        {
            var delement = IterateOverElement(element);
            if (((element.ElementSchemaType is XmlSchemaSimpleType)))
            {
                ((IDictionary<string, Object>)delement).Remove("description");
                ((IDictionary<string, Object>)delement).Add("description", HandleAnnotation(element.Annotation));
            }
            else
            {
                ((IDictionary<string, Object>)delement).Add("description", HandleAnnotation(element.Annotation));
                if (!element.SchemaTypeName.IsEmpty)
                {
                    ((IDictionary<string, Object>)delement).Remove("type");
                    ((IDictionary<string, Object>)delement).Add("type", element.SchemaTypeName.Name);
                    ((IDictionary<string, Object>)delement).Add("extends", new ExpandoObject());
                    ((IDictionary<string, Object>)delement.extends).Add("$ref", element.SchemaTypeName.Namespace + "/" + element.SchemaTypeName.Name + "#");
                }
                else
                {
                    if (element.ElementSchemaType.BaseXmlSchemaType == null)
                    {
                        ((IDictionary<string, Object>)delement).Add("type", element.ElementSchemaType.QualifiedName.Name);
                        ((IDictionary<string, Object>)delement).Add("extends", new ExpandoObject());
                        ((IDictionary<string, Object>)delement.extends).Add("$ref", element.ElementSchemaType.QualifiedName.Namespace + "/" + element.ElementSchemaType.QualifiedName.Name + "#");
                    }
                    else
                    {
                        ((IDictionary<string, Object>)delement).Add("type", element.ElementSchemaType.BaseXmlSchemaType.QualifiedName.Name);
                        ((IDictionary<string, Object>)delement).Add("extends", new ExpandoObject());
                        ((IDictionary<string, Object>)delement.extends).Add("$ref", element.ElementSchemaType.BaseXmlSchemaType.QualifiedName.Namespace + "/" + element.ElementSchemaType.BaseXmlSchemaType.QualifiedName.Name + "#");
                    }
                }
            }
            if (!element.SubstitutionGroup.IsEmpty)
                ((IDictionary<string, Object>)delement).Add("substitutiongroup", element.SubstitutionGroup.Namespace + "/" + element.SubstitutionGroup.Name + "#");
            if (element.IsAbstract)
                ((IDictionary<string, Object>)delement).Add("abstract", element.IsAbstract);
            if (element.IsNillable)
                ((IDictionary<string, Object>)delement).Add("nillable", element.IsNillable);

            ((IDictionary<string, Object>)delement).Add("maxItems", element.MaxOccurs);
            ((IDictionary<string, Object>)delement).Add("minItems", element.MinOccurs);

            return delement;
        }

        public static void PopulateJsonSchema(string filename , string filepath)
        {
            Console.WriteLine("------"+filename);
            var targetNamespace =  GetTargetSchema(filepath);
            dynamic jsonschema = new ExpandoObject();
            var schemaSet = new XmlSchemaSet();
            schemaSet.ValidationEventHandler += ValidationCallback;
            schemaSet.Add(targetNamespace, filepath);
            schemaSet.Compile();

            //Populate Header Here
            var baseschema = schemaSet.Schemas(targetNamespace).Cast<XmlSchema>().First();

            ((IDictionary<string, Object>)jsonschema).Add("$schema", baseschema.TargetNamespace);
            jsonschema.version = baseschema.Version;
            jsonschema.name = filename.Substring(0, filename.Length - 4);

            //Populate description of the JsonSchemaObject
            foreach (var obj in schemaSet.Schemas(targetNamespace).Cast<XmlSchema>().SelectMany(s => s.Items.OfType<XmlSchemaAnnotation>())){
                jsonschema.description =  GetSchemaDescription(obj);
            }

            jsonschema.properties = new Dictionary<String, Object>();

            foreach (var element in schemaSet.Schemas(targetNamespace).Cast<XmlSchema>().SelectMany(s => s.Elements.Values.Cast<XmlSchemaElement>()))
            {
                Console.WriteLine(element.Name);
                jsonschema.properties.Add(element.Name, FixElement(element));
            }

            //Get all the complextypes

            foreach (var obj in
                schemaSet.Schemas(targetNamespace).Cast<XmlSchema>().SelectMany(s => s.Items.OfType<XmlSchemaComplexType>()))
            {
                //.Where(obj => obj.ContentModel != null)
                Console.WriteLine(obj.Name);
                var d = HandleComplexType(obj);
                jsonschema.properties.Add(obj.QualifiedName.Name, d);
            }

            //get all the Simple Types
            foreach (var obj in
               schemaSet.Schemas(targetNamespace).Cast<XmlSchema>().SelectMany(s => s.Items.OfType<XmlSchemaSimpleType>().Where(c => c.Content != null)))
            {
                Console.WriteLine(obj.Name);
                var d = HandleSimpleType(obj);
                jsonschema.properties.Add(obj.QualifiedName.Name, d);
            }

            //get all the attributeGroups

            foreach (var obj in
               schemaSet.Schemas(targetNamespace).Cast<XmlSchema>().SelectMany(s => s.Items.OfType<XmlSchemaAttributeGroup>()))
            {
                Console.WriteLine(obj.Name);
                jsonschema.properties.Add(obj.QualifiedName.Name,HandleAttributeGroup(obj));
            }

            //get all the attributes
            foreach (var attr in from XmlSchema schema in schemaSet.Schemas(targetNamespace)
                                 let temp = schema.Attributes
                                 let schema1 = schema
                                 from attr in temp.Names.Cast<XmlQualifiedName>().Select(name => name)
                                        .Select(t => schema1.Attributes[t] as XmlSchemaAttribute).
                                        Where(attr => attr.AttributeSchemaType != null)
                                 select attr)
            {
                jsonschema.properties.Add(attr.Name, HandleAttribute(attr));
            }

               String json = JsonConvert.SerializeObject(jsonschema, Newtonsoft.Json.Formatting.Indented);
               File.WriteAllText( filepath.Replace(filename, jsonschema.name + ".js"), json.Replace("Hello", "").Replace(@"\n","").Replace(@"\r","").Replace(@"\t",""));
        }
      }
 }

 static void Main(string[] args)
        {       
            const string path = @"C:\Users\ashutosh\documents\visual studio 2010\Projects\XSDToJson\XSDToJson";
            var files = NavigateDirectories(path);
            foreach (var file in files)
            {
                 ReadXsd.PopulateJsonSchema(file.Name, file.FullName);
            }
            Console.WriteLine("Press any key to exit");              

        }
  1. As you would notice I am using an IDictionary to add the properties to JSON Schema. So, when I have a situation where there are two properties with the same name even though they come from two different target schema, I am stuck. The "Hello" written there is just to let me finish emitting out jsonschema. I would like to handle this schema, without making some serious breaking changes to this code.

  2. What design pattern could I use and is this also a case where I can refactor some set of methods into extension methods?

\$\endgroup\$
  • \$\begingroup\$ Kind of late, but why not use Json.NET? Just use xsd2code and then JSchemaGenerator generator = new JSchemaGenerator(); JSchema schema = generator.Generate(typeof(...)); \$\endgroup\$ – Lucas B Nov 5 '15 at 16:08
4
\$\begingroup\$

I have not tried or scrutinized your code much, but my immediate impression is that you can clean this up quite a bit.

I recommend getting this book about clean code: http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882

If you look at your HandleSimpleTypeRestriction method, you repeat the same 5 lines for each foreach statement. By extracting those five lines to a method, you'll shorten the HandleSimpleTypeRestriction method by 12 lines. The code will end up like this:

    private static Dictionary<String, Object> HandleSimpleTypeRestriction(XmlSchemaSimpleTypeRestriction restriction) 
    { 
        var res = new Dictionary<String, Object>(); 
        foreach (var t in restriction.Facets.OfType<XmlSchemaEnumerationFacet>()) 
            AddRes(res, t);

        foreach (var t in restriction.Facets.OfType<XmlSchemaMaxExclusiveFacet>()) 
            AddRes(res, t);

        foreach (var t in restriction.Facets.OfType<XmlSchemaMinExclusiveFacet>()) 
            AddRes(res, t);

        return res; 
    } 

    private static void AddRes<T>(Dictionary<string, object> res, T t)
    // you're gonna need a where T : somebaseclass here.
    {
            var d = new Dictionary<String, Object>{{"fixed", t.IsFixed}}; 
            var annotation = HandleAnnotation(t.Annotation); 
            if (annotation != "") 
                d.Add("description", annotation); 
            res.Add(t.Value,d); 
    }

You could even put the for in a method so you end up with an even shorter method.

Rinse and repeat for all your code, and I bet you've got a much more readable class.

Then you can see if some of the methods share parameters that others don't use, and separate those into smaller utility classes with one responsibility. See http://en.wikipedia.org/wiki/Single_responsibility_principle (a very good practice)

All this gives you code that is easier to understand and maintain. Should be done with care though, if you don't do unit testing, look into it! :)

\$\endgroup\$
  • \$\begingroup\$ thanks for the input...this seems like the way to go ..i know about SOLID ...i am just wondering if re-factoring before i have all the functionality going is a great idea ? \$\endgroup\$ – ashutosh raina Dec 25 '11 at 5:43
  • 3
    \$\begingroup\$ I believe you should work test-driven and refactor more or less all the time. The day consists of 1. Write some test code 2. Write some prod code 3. Repeat from step 1 until something messy starts to work 4. Refactor 5. Repeat steps 1-5 \$\endgroup\$ – Lars-Erik Dec 27 '11 at 22:17
  • \$\begingroup\$ i have taken your suggestions into account and then used some of my own searching to make it slightly more readable and extensible. The core class(ReadXsd) has reduced by more than a 100 lines . The code has been updated with v 0.3. \$\endgroup\$ – ashutosh raina Dec 29 '11 at 12:23
  • 3
    \$\begingroup\$ +1 for a very solid answer. In addition to Robert C. Martin's Clean Code that you've already suggested, I'd like to also recommend Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce. \$\endgroup\$ – Adam Dec 30 '11 at 12:53
  • \$\begingroup\$ @codesparkle my updated question should make it a little easier for the adventurous souls :) Give it a shot \$\endgroup\$ – ashutosh raina Dec 30 '11 at 19:01

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.