Serialize GraphicsLayer

3276
10
06-09-2010 07:24 AM
RyanCoodey
Occasional Contributor III
So I have tried to serialize a GraphicsLayer, a GraphicCollection and just a Graphic itself.  I have tried using XmlSerializer, DataContractSerializer and DataContractJsonSerializer... all of which give some sort of error.

I want the users to be able to save any graphic markup they have made on the map, so I want to serialize them... is there no way to do this?

Thanks a lot!

*EDIT*  Also, I read the article: http://blogs.esri.com/Dev/blogs/silverlightwpf/archive/2010/03/11/Sending-geometry-between-Silverlig..., but don't want/need it sent to the server... it just needs to be saved for that one user
10 Replies
dotMorten_esri
Esri Notable Contributor
You cannot serialize Symbol, because you cannot serialize the brushes it contains.
If you know you are only dealing with for instance SimpleMarkerSymbol that uses SolidColorBrush, you could manually serialize this. Ie create a new object that only contains properties you can serialize:
 
public class SerializableFeature {
   public SerializableFeature(Graphic g) {
     Geometry = g.Geometry;
     Attributes = g.Attributes;
     Color = ((g.Symbol as SimpleMarkerSymbol).Color as SolidColorBrush).Color;
   }
   public Geometry Geometry { get; set; }
   public Color Color { get; set; }
   public IDictionary<string,object> Attributes { get; set; }
}

You can then extend this to support other types of symbols (would probably require some more properties)
0 Kudos
RyanCoodey
Occasional Contributor III
Great, thanks Morten...

Trying to get this going, but a couple things:
1) Graphic.Attributes: I dont think i need this even, but one it wont serialize and two it is read only so I can't set it after it is saved.  but as i said, I dont think I need this anyway...
2) Geometry wont serialize either, throws:
System.InvalidOperationException: There was an error generating the XML document. --->
System.InvalidOperationException: The type ESRI.ArcGIS.Client.Geometry.Polyline was not expected. Use the XmlInclude attribute to specify types that are not known statically.
Didn't try with a DataContractSerializer, should I?  I prefer the Xml if possible...

*EDIT* DataContractSerializer DOES work!!!  hmmm... breaks a few other things I am serializing with the Xml though, have to see how to fix those.  Also, the Attributes work too, still not sure how to set them back though
0 Kudos
dotMorten_esri
Esri Notable Contributor
DataContractSerializer works because we define datacontracts on Geometry, so yes that works. Just define a datacontract on the entire SerializableFeature, and you should be good to go using that for the entire thing
.
To convert back to graphic, and setting attributes, you can add this little ToGraphic utility method to your class:
 
[DataContract]
public class SerializableFeature {
   public Graphic ToGraphic() {
      Graphic g = new Graphic() { Geometry = Geometry };
      foreach(string key in Attributes.Keys)
           g.Attributes.Add(key, Attributes[key]);
     g.Symbol = new SimpleMarkerSymbol() { Color = new SolidColorBrush(Color) };
     return g;
   }
}
0 Kudos
RyanCoodey
Occasional Contributor III
Thanks a lot for all the help Morten!

If it helps anyone else, here are a few classes I made that work with the three different SimpleXXXSymbol types:
    public class SerializableSymbol
    {
        //Properties
        public Color ColorFill { get; set; }
        public Color BorderBrush { get; set; }
        public Double SizeThickness { get; set; }
        public Int32 Style { get; set; }
        public SymbolType Type { get; set; }
        public enum SymbolType
        {
            SimpleMarkerSymbol = 0,
            SimpleLineSymbol = 1,
            SimpleFillSymbol = 2
        }

        //Constructors
        public SerializableSymbol()
        {
        }
        public SerializableSymbol(Symbol symbol)
        {
            if (symbol is SimpleMarkerSymbol)
            {
                SimpleMarkerSymbol simpleMarkerSymbol = ((SimpleMarkerSymbol)symbol);
                ColorFill = ((SolidColorBrush)simpleMarkerSymbol.Color).Color;
                SizeThickness = simpleMarkerSymbol.Size;
                Style = (Int32)simpleMarkerSymbol.Style;
                Type = SymbolType.SimpleMarkerSymbol;
            }
            else if (symbol is SimpleLineSymbol)
            {
                SimpleLineSymbol simpleLineSymbol = ((SimpleLineSymbol)symbol);
                ColorFill = ((SolidColorBrush)simpleLineSymbol.Color).Color;
                SizeThickness = simpleLineSymbol.Width;
                Style = (Int32)simpleLineSymbol.Style;
                Type = SymbolType.SimpleLineSymbol;
            }
            else if (symbol is SimpleFillSymbol)
            {
                SimpleFillSymbol simpleFillSymbol = ((SimpleFillSymbol)symbol);
                ColorFill = ((SolidColorBrush)simpleFillSymbol.Fill).Color;
                BorderBrush = ((SolidColorBrush)simpleFillSymbol.Fill).Color;
                SizeThickness = simpleFillSymbol.BorderThickness;
                Type = SymbolType.SimpleFillSymbol;
            }
        }

        //Operators
        public static explicit operator SerializableSymbol(Symbol symbol)
        {
            SerializableSymbol serializableSymbol = new SerializableSymbol(symbol);
            return serializableSymbol;
        }
        public static implicit operator Symbol(SerializableSymbol serializableSymbol)
        {
            if (serializableSymbol.Type == SymbolType.SimpleMarkerSymbol)
            {
                SimpleMarkerSymbol symbol = new SimpleMarkerSymbol();
                symbol.Color = new SolidColorBrush(serializableSymbol.ColorFill);
                symbol.Size = serializableSymbol.SizeThickness;
                symbol.Style = (SimpleMarkerSymbol.SimpleMarkerStyle)serializableSymbol.Style;
                return symbol;
            }
            else if (serializableSymbol.Type == SymbolType.SimpleLineSymbol)
            {
                SimpleLineSymbol symbol = new SimpleLineSymbol();
                symbol.Color = new SolidColorBrush(serializableSymbol.ColorFill);
                symbol.Width = serializableSymbol.SizeThickness;
                symbol.Style = (SimpleLineSymbol.LineStyle)serializableSymbol.Style;
                return symbol;
            }
            else if (serializableSymbol.Type == SymbolType.SimpleFillSymbol)
            {
                SimpleFillSymbol symbol = new SimpleFillSymbol();
                symbol.Fill = new SolidColorBrush(serializableSymbol.ColorFill);
                symbol.BorderBrush = new SolidColorBrush(serializableSymbol.BorderBrush);
                symbol.BorderThickness = serializableSymbol.SizeThickness;
                return symbol;
            }
            else
                return null;
        }
    }

    public class SerializableGraphic
    {
        //Properties
        public ESRI.ArcGIS.Client.Geometry.Geometry Geometry { get; set; }
        public SerializableSymbol Symbol { get; set; }
        public IDictionary<String, object> Attributes { get; set; }

        //Constructor
        public SerializableGraphic()
        {
        }
        public SerializableGraphic(Graphic graphic)
        {
            Geometry = graphic.Geometry;
            Attributes = graphic.Attributes;
            Symbol = new SerializableSymbol(graphic.Symbol);
        }

        //Operators
        public static explicit operator SerializableGraphic(Graphic graphic)
        {
            SerializableGraphic serializableGraphic = new SerializableGraphic(graphic);
            return serializableGraphic;
        }
        public static implicit operator Graphic(SerializableGraphic serializableGraphic)
        {
            Graphic graphic = new Graphic
            {
                Geometry = serializableGraphic.Geometry,
                Symbol = serializableGraphic.Symbol,
            };
            foreach (String key in serializableGraphic.Attributes.Keys)
                graphic.Attributes.Add(key, serializableGraphic.Attributes[key]);
            return graphic;
        }
    }
0 Kudos
FrancoisChartrand
New Contributor III
I also need a way to persist the GraphicsLayers. It would be great if this functionality (serialization or whatever) would be embedded in the API.

Thank you Ryan for sharing your code, it was very useful for me.
0 Kudos
dotMorten_esri
Esri Notable Contributor
I would agree. However it is impossible to have at the API level because there are parts of the graphics that simply are not serializable (not only symbols but even attributes can be an issue) not to mention custom extensions like custom renderers and graphics layers etc.
It can work on an app level where you might know what limited set of symbols and attribute types you have.
0 Kudos
DominiqueBroux
Esri Frequent Contributor
There is an example of GraphicsLayer serialization here : http://forums.arcgis.com/threads/8774-save-layer-to-xml-file?highlight=serialize+graphics but there are some limitations : for example the symbols are not serialized.
0 Kudos
AndyWright
Occasional Contributor
Extremely useful stuff Ryan!  Thanks a ton for the post!
0 Kudos
linusang
New Contributor II
anyone tried to do the serialization operation asynchronously?

I tried using BackgroundWorker and Task and stumbled upon the "The calling thread cannot access this object because a different thread owns it." exception because the GraphicsLayer object is created in another thread (thread affinity).

i'm using WPF btw...
0 Kudos