-1

Autodesk Revit Development

I serialized an XYZ point form a class (Points) from a container (Points and Tags) to a file.

public class Serialize_pack
{
    public View_3D_Data v3ddata;
    public Tag_Class tg;
}

through this Method

public static void serializeme(object classme)
{
    string coor_file = constants.SenddDir() + constants.filename();

    using (StreamWriter file = File.CreateText(coor_file))
    {
        JsonSerializer serializer = new JsonSerializer();

        serializer.Serialize(file, classme);
    }
}

and i got this results

{"Points":{"bboxmin":{"Z":-1000.0,"Y":-100.0,"X":-100.0},"bboxmax":{"Z":-0.1,"Y":100.0,"X":100.0}},"tg":{"eId":"666470","text":"coor: Kiss me"}}

on deserializing i got the results for all the points to a value of (0.0,0.0,0.0) which is a result of unable to parse read values to its propitiate type.

Deserializing Method

public static object deserializeme(string path)
{
    Serialize_pack accquired = null;
    using (StreamReader file = File.OpenText(path))
    {
        JsonSerializer serializer = new JsonSerializer();
        accquired = (Serialize_pack)serializer.Deserialize(file, typeof(Serialize_pack));                           
    }

    return accquired;
}

I wish i could find a way a good way to convert and override this mess.

Edit: Exact Newton.JSon OutPut

{"Points":{"bboxmin":{"Z":-1000.0,"Y":-100.0,"X":-100.0},"bboxmax":{"Z":-0.1,"Y":100.0,"X":100.0},"sboxmin":{"Z":-10.277690406517843,"Y":-13.533464566929133,"X":-13.389107611548557},"sboxmax":{"Z":16.510826771653544,"Y":13.533464566929133,"X":13.389107611548557},"vorEyP":{"Z":30.114082470913921,"Y":34.471718543415037,"X":-7.7202528373680934},"vorFwD":{"Z":-0.57735026918962573,"Y":-0.57735026918962584,"X":0.57735026918962573},"vorUP":{"Z":0.816496580927726,"Y":-0.408248290463863,"X":0.40824829046386296},"v3dname":"Arch_Moustafa-GAJ-145834"},"Tags":{"eId":"666470","origin":{"Z":1154.5239372729186,"Y":1164.3934060532893,"X":-1119.6229882673815},"text":"coor: Kiss me","ledelbo":{"Z":1157.6807845880096,"Y":1163.9955344285622,"X":-1116.8640125770175}}}

Tag Class

public class Tag
{
    public string eId;

    public XYZ origin;
    public string text;
    public XYZ ledelbo;

public void getTagdata(View v)
    {
        ///we need all the annotation to be sent.
       /// do some stuff and cast the results on the public variables

    }
}

Points Class

public class Points
{

    public XYZ bboxmin;
    public XYZ bboxmax;
    public XYZ sboxmin;
    public XYZ sboxmax;

    public XYZ vorEyP;
    public XYZ vorFwD;
    public XYZ vorUP;

    public string v3dname;


    [JsonIgnore]
    public View3D view;

 public void get3dviewdata()
    {
       ///we need all the points to be sent.
       /// do some stuff and cast the results on the public variables
    }
}
M.Khalil
  • 33
  • 2
  • 7
  • Your Json is invalid, according to http://jsonformatter.curiousconcept.com/. Errors include "Extra closing }" at the end of the file. Did you paste in all of the Json output, or was some skipped? – dbc Aug 24 '14 at 13:46
  • skipped some, to make it easier..if you see it is essential to put it all...i will do? – M.Khalil Aug 24 '14 at 13:52
  • I'd suggest making a [minimal, complete, and verifiable example](http://stackoverflow.com/help/mcve) of your problem. If your Json doesn't parse because you copied it incompletely, then it's not complete or verifiable. – dbc Aug 24 '14 at 13:55
  • ok I'll update the Json OutPut – M.Khalil Aug 24 '14 at 13:58
  • i have also add now the rest of classes for more clarification. – M.Khalil Aug 24 '14 at 14:26

1 Answers1

2

OK, the problem here seems to be that the XYZ class in Revit is immutable, so the JsonSerializer cannot set the properties. Under normal circumstances the way to deal with this is to decorate an appropriate constructor for your class with JsonConstructorAttribute - but you cannot do that because XYZ is a Revit class, not your own.

Solution #1

To work around this problem, you could subclass XYZ and decorate the appropriate constructor with the attribute - however, I'm not sure whether the Revit class is sealed, or whether this could have unforseen side effects if you actually pass one of these subclased XYXs back to Revit. Alternatively, you could introduce a proxy class purely for serialization and deserialization:

public static class XYZProxyExtensions
{
    public static XYZProxy ToXYZProxy(this XYZ xyz)
    {
        return new XYZProxy(xyz.X, xyz.Y, xyz.Z);
    }
}

public class XYZProxy
{
    public XYZProxy()
    {
        this.X = this.Y = this.Z = 0;
    }

    public XYZProxy(double x, double y, double z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }

    public XYZ ToXYZ()
    {
        return new XYZ(X, Y, Z);
    }
    public override string ToString()
    {
        return string.Format("({0},{1},{2})", X, Y, Z);
    }
}

Having done this, you can then add proxy properties to your custom classes, mark them hidden in the debugger, and tell Json.Net to serialize the proxies, not the original properties:

[JsonObject(MemberSerialization.OptIn)]
public class Points
{
    public XYZ bboxmin { get; set; }

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    [JsonProperty(PropertyName = "bboxmin")]
    public XYZProxy bboxminProxy
    {
        get
        {
            return bboxmin.ToXYZProxy();
        }
        set
        {
            bboxmin = value.ToXYZ();
        }
    }
}

More information here: http://www.tecsupra.com/serializing-only-some-properties-of-an-object-to-json-using-newtonsoft-json-net/ and here: How can I change property names when serializing with Json.net?

Solution #2

Alternatively, you might try writing your own JsonConverter for XYZ and then registering it with Json.Net. The converter might look something like this (warning - not tested!)

class XYZConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(XYZ);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = JToken.Load(reader);
        if (obj.Type == JTokenType.Array)
        {
            var arr = (JArray)obj;
            if (arr.Count == 3 && arr.All(token => token.Type == JTokenType.Float))
            {
                return new XYZ(arr[0].Value<double>(), arr[1].Value<double>(), arr[2].Value<double>());
            }
        }
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var vector = (XYZ)value;
        writer.WriteStartArray();
        writer.WriteValue(vector.X);
        writer.WriteValue(vector.Y);
        writer.WriteValue(vector.Z);
        writer.WriteEndArray();
    }
}

which would produce Json that looks like this:

"bboxmin": [ -100.0, -100.0, -1000.0 ]

This format might be better or worse; are you trying to read and write files to some pre-existing 3rd party library?]

2nd Solution Update

You need to create the JsonSerializer with the appropriate settings in order to invoke the converter, simply doing new JsonSerializer() will not work. I tested the following and it works fine (here my version of your class Points has only 4 fields):

public static class JsonSerializerTest
{
    static JsonSerializerTest()
    {
        // This needs to be done only once, so put it in an appropriate static initializer.
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> { new XYZConverter() }
        };
    }

    public static void Test()
    {
        Points points = new Points();
        points.bboxmin = new XYZ(-100, -100, -1000);
        points.bboxmax = new XYZ( 100,  100,  1000);
        points.sboxmin = new XYZ(-10, -10, -100);
        points.sboxmax = new XYZ( 10, 10, 100);

        try
        {
            string json;
            using (var writer = new StringWriter())
            {
                JsonSerializer serializer = JsonSerializer.CreateDefault();
                serializer.Serialize(writer, points);
                json = writer.ToString();
            }

            Points newPoints = null;
            using (var reader = new StringReader(json))
            {
                JsonSerializer serializer = JsonSerializer.CreateDefault();
                newPoints = (Points)serializer.Deserialize(reader, typeof(Points));
            }

            Debug.Assert(points.bboxmin.IsAlmostEqualTo(newPoints.bboxmin));
            Debug.Assert(points.bboxmax.IsAlmostEqualTo(newPoints.bboxmax));
            Debug.Assert(points.sboxmin.IsAlmostEqualTo(newPoints.sboxmin));
            Debug.Assert(points.sboxmax.IsAlmostEqualTo(newPoints.sboxmax));
        }
        catch (Exception ex)
        {
            Debug.Assert(false, ex.ToString());
        }
    }
}

The Json output produced is quite simple and readable:

{"bboxmin":[-100.0,-100.0,-1000.0],"bboxmax":[100.0,100.0,1000.0],"sboxmin":[-10.0,-10.0,-100.0],"sboxmax":[10.0,10.0,100.0]}

This avoids the requirement for proxies and so is probably a prettier solution.

Community
  • 1
  • 1
dbc
  • 104,963
  • 20
  • 228
  • 340
  • regarding your last qyestion, no i will not use it with a 3d part, in fact i want to export some tiny tasks from Revit to be sent via Internet to another user which is about 8 kb or something than sending the whole 160 MB file. I tried the 1st Solution and it works nicely... tring now the second one. i wish you could guide me on how to use JSonConverter if you know some sites that have a friendly illustration, would be nice. – M.Khalil Aug 24 '14 at 16:51
  • @M.Khalil - see instructions for how to use the 2nd solution. – dbc Aug 24 '14 at 17:03