0

I have a class, that has an object:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34283")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class Scores : baseModel
{
   private System.Xml.XmlAttribute[] anyAttrField;
   /// <remarks/>
   [System.Xml.Serialization.XmlAnyAttributeAttribute()]
   public System.Xml.XmlAttribute[] AnyAttr
  {
      get
      {
         return this.anyAttrField;
      }
      set
      {
         this.anyAttrField = value;
      }
   }
}

From the parent class (snippet of it):

public parial class LandingPage : baseModel
{
    private string projectNameField;
    private Scores scoresField;
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string projectName
    {
        get { return this.projectNameField; }
        set { this.projectNameField = value; }
    }
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public Scores scores
    {
        get { return this.scoresField; }
        set { this.scoresField = value }
    }
}

The JSON string I'm trying to work with:

{
    "projectName":"PROJECTTEST",
    "scores":{
        "browLocker":100,
        "heavyAd":0,
        "walletRedirection":0
    }
}

NewtonSoft.JsonConvert ignores the scores child fields...

How can I easily convert this to work?

Joe Kuzma
  • 13
  • 2
  • 3
    Why would Json.NET care about an XML attribute? Why don't you create a pair of classes that actually matches the JSON document? – Panagiotis Kanavos May 09 '17 at 14:52
  • 1
    Why are you making your life harder by trying to convert JSON to XML when you can just work with the JSON you have? – Rafael May 09 '17 at 14:55
  • Change your `Scores` class to `class Scores{public int BrowLocker {get;set;} public int HeavyAd {get;set;} public int WalletRedirection{get;set;}}`. You don't need anything else to deserialize this string – Panagiotis Kanavos May 09 '17 at 14:58
  • Thanks... the problem is the results may include additional elements.. so how do I handle something that can have 1 of the elements, or 5... – Joe Kuzma May 09 '17 at 15:06
  • Your question is a bit unclear to me. It looks like you are trying to deserialize some JSON into some classes that were generated for working with XML. Is it a requirement that you use these specific classes, or are you free to use other classes if it makes things easier to deserialize? Also, when you say "something can have 1 of the elements or 5", what are you referring to? – Brian Rogers May 09 '17 at 15:13
  • Does `Scores` have any properties besides the `AnyAttr` member that need to be serialized from and to JSON? – dbc May 09 '17 at 17:03

1 Answers1

0

Given that your Scores type has no properties of its own to serialize, you can create a custom JsonConverter for it that converts the XmlAttribute [] array to a Dictionary<string, string> then serializes that dictionary in place of the Scores object:

public class XmlAttributeArrayConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(XmlAttribute[]);
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var dict = serializer.Deserialize<Dictionary<string, string>>(reader);
        var doc = new XmlDocument();
        return dict.Select(p => { var a = doc.CreateAttribute(p.Key); a.Value = p.Value; return a; }).ToArray();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // TODO: determine how to represent XmlAttribute values with non-empty NamespaceURI - or throw an exception.
        var attributes = (IEnumerable<XmlAttribute>)value;
        var dict = attributes.ToDictionary(a => a.Name, a => a.Value);
        serializer.Serialize(writer, dict);
    }
}

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var attributes = (XmlAttribute[])new XmlAttributeArrayConverter().ReadJson(reader, typeof(XmlAttribute[]), null, serializer);
        return new Scores { AnyAttr = attributes };
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var scores = (Scores)value;
        if (scores.AnyAttr == null)
            writer.WriteNull();
        else
        {
            new XmlAttributeArrayConverter().WriteJson(writer, scores.AnyAttr, serializer);
        }
    }
}

(Note I extracted some of the logic into a separate XmlAttributeArrayConverter in case it might be of use elsewhere.)

Then apply it to Scores as follows:

[JsonConverter(typeof(ScoresConverter))]
public partial class Scores
{
}

Sample fiddle.

Note if one of the XML attributes happens to be in a namespace (most of the time none will be) then the namespace is not serialized. If your attributes might have namespaces you will need to decide how to convert the attribute's full name to a JSON property name. {namespace}localname would be one possibility, that is what is used by XName.ToString().

dbc
  • 104,963
  • 20
  • 228
  • 340