6
\$\begingroup\$

I have zones

public class Zone
{
     public string Caption { get; set; }
     public IList<Module> Contents { get; set; }        
}

public class Zone1 : Zone
{
}
public class Zone2 : Zone
{
}
public class Zone3 : Zone
{
    public enum Direction
    {
        Up = 1,
        Down = 2,
        Left = 3,
        Right = 4
    }
    public Direction Direction { get; set; }
}

I have json string [{"Caption":"Tab1","IDs":[4,6]},{"Caption":"Tab2","IDs":[9]}] that corresponds to the regular zones like Zone1,Zone2

{"Caption":"Tab1","IDs":[4,6]} means that the name of this zone is Tab1 and it will contain Modules #4 and #6.

I have default parser that will create modules according to IDs(4,6 AND 9).

public interface IZoneParser<T>
{
    IList<T> Parse(string json);    
}
public abstract class ZoneParserBase : IZoneParser<Zone>
{
    public virtual IList<Zone> Parse(string json)
    {
        IList<Zone> zones = new List<Zone>();
        try
        {
            IList<ZoneJson> parsedZones = json.FromJSON<IList<ZoneJson>>();

            foreach (var parsedZone in parsedZones)
            {
                var zone = new Zone
                {
                    Caption = parsedZone.Caption,
                    Contents = new List<Module>()
                };
                foreach (var id in parsedZone.IDs)
                {
                    var module = currentPage.CreateModule(id);
                    zone.Contents.Add(module);
                }
                zones.Add(zone);
            }

            return zones;
        }
        catch
        {
            return null;
        }
    }
}

So, in order to parse the above json I'll need some class like

[DataContract]  
private class ZoneJson
{
     [DataMember]
     public string Caption { get; set; }
     [DataMember]
     public IList<int> IDs { get; set; }
}

If I'll need to parse Zone3 with json string [{"Caption":"Tab1","IDs":[4,6],"Direction":"1"},{"Caption":"Tab2","IDs":[9]},"Direction":"3"], so I'll create

public class ZoneParserA : ZoneParserBase
{
   publice override IList<Zone3> Parse(string json)
   {
       ....
       IList<Zone3Json> parsedZones = json.FromJSON<IList<Zone3Json>>();

       foreach (var parsedZone in parsedZones)
       {
           var zone = new Zone3
           {
                Caption = parsedZone.Caption,
                Direction = parsedZone.Direction,
                Contents = new List<Module>()
           };
           foreach (var id in parsedZone.IDs)
           {
                var module = currentPage.CreateModule(id);
                zone.Contents.Add(module);
           }
           zones.Add(zone);
       }
       ....
   }
}

and

[DataContract]
[KnownType(typeof(Direction))]
public class Zone3Json : ZoneJson
{        
    public Direction Direction { get; set; }
}

So, for each type of Zone(Zone,Zone1,Zone2,Zone3...) I'll need some kind Zone(X)Json that will differ from Zone(X) just with IList<int> IDs property. I can add this property to class Zone, but I don't want that anybody could access it from outside.

In addition, the functionality of method Parse is more or less the same for ZoneParserA and ZoneParserBase.

Is there way to avoid Zone(X)Json classes and/or maybe there are any suggestions to rewriting ZoneParsers?

I hope I explained myself properly.

Thank you

\$\endgroup\$
  • \$\begingroup\$ Have you tried gson? It will probably work for Zone as well, and if not, you can create custom serializers. \$\endgroup\$ – Neil Jul 11 '11 at 13:12
  • \$\begingroup\$ @Nell, butt I still have to crate Zone & ZoneJson, Zone3 & Zone3Json, Zone2 & Zone2Json..., right? \$\endgroup\$ – theateist Jul 11 '11 at 14:28
  • \$\begingroup\$ You can, yes, though you can just as easily load everything in a map and use gson.toJson(map) to convert it. \$\endgroup\$ – Neil Jul 11 '11 at 16:00
  • \$\begingroup\$ @Neil, that's not I want! Currently for every new ZoneX type I'll add I will need to create ZoneXJson class that will be the same as ZoneX but with ONE additional property 'IDs'. And I don't want to "duplicate" the classes \$\endgroup\$ – theateist Jul 11 '11 at 18:42
  • \$\begingroup\$ You can add the IDs to the ZoneX classes and you can hide the conversion to JSON of these IDs by marking it as transient. You can accomplish it in other ways as well if you didn't want to mark it transient. \$\endgroup\$ – Neil Jul 12 '11 at 8:51
2
\$\begingroup\$

I can add this property to class Zone, but I don't want that anybody could access it from outside.

Which 'access' do you have in mind? through JSON (send you a 'fake' direction for zone1,2) or through code?

Either way, I don't think that by not adding it to the Zone class you will improve the security of your solution?

How do you decide on which parser to use when you receive a json string? BTW, your second json string looks wrong to me (the last direction param)

[{"Caption":"Tab1","IDs":[4,6],"Direction":"1"},{"Caption":"Tab2","IDs":[9]},"Direction":"3"]

Why do you need Zone1 and Zone2? They're identical so why not simply use Zone? Even your ZoneBaseParser creates Zone objects and has no notion of Zone1 or Zone2.

Maybe you have a predefined set of zones (up to 4) and you need an index. Add an index property, if that's the case.

Your question is interesting but, instead of implementing a solution to the current problem (which would involve reflection), I'd focus on rethinking the json format and the classes and remove the problem altogether.

Quick and easy:

[DataContract]  
private class ZoneJson
{
     [DataMember]
     public int ZoneType { get; set; }//could use an enum instead of int
     [DataMember]
     public int ZoneIndex { get; set; }//if a zone index is needed
     [DataMember]
     public string Caption { get; set; }
     [DataMember]
     public IList<int> IDs { get; set; }
     [DataMember]
     public Direction Direction { get; set; }
}

Or, a more generic approach:

[DataContract]  
private class ZoneJson
{
     [DataMember]
     public int ZoneType { get; set; }//could use an enum instead of int
     [DataMember]
     public int ZoneIndex { get; set; }//if a zone index is needed
     [DataMember]
     public string Caption { get; set; }
     [DataMember]
     public IList<int> IDs { get; set; }


     //a json string containing additional params
     //might be set for certain ZoneTypes and it's contents can vary, based on ZoneType
     [DataMember]
     public string JsonParams { get; set; }
}

Next, you can add a Zone3Params class to implement the storage for Zone3 params (direction):

[DataContract]
[KnownType(typeof(Direction))]
public class Zone3ParamsJson : ZoneParamsBase
{        
    [DataMember]
    public Direction Direction { get; set; }
}

and parse any defined zone type like this:

public class ZoneParserBase : IZoneParser<Zone>
{
    public virtual IList<Zone> Parse(string json)
    {
        IList<Zone> zones = new List<Zone>();
        try
        {
            IList<ZoneJson> parsedZones = json.FromJSON<IList<ZoneJson>>();

            foreach (var parsedZone in parsedZones)
            {
                var zone = new Zone
                {
                    //don't forget the index and type
                    ZoneType = parsedZone.ZoneType,
                    Caption = parsedZone.Caption,
                    Contents = new List<Module>()
                };
                foreach (var id in parsedZone.IDs)
                {
                    var module = currentPage.CreateModule(id);
                    zone.Contents.Add(module);
                }
                //parseParams
                if(zone.ZoneType==3)
                {
                    var params = parsedZone.JsonParams.FromJSON<Zone3ParamsJson>();
                    zone.Params = params;//zone.Params would be something like ZoneParamsBase
                }
                zones.Add(zone);
            }

            return zones;
        }
        catch
        {
            return null;
        }
    }
}
\$\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.