Domanda

I have a java library that contains all the domain models for our backend REST API. The backend API is implemented in Java and translate Java objects to JSON using Jackson.

Recently we need to implement a new feature and have a Windows .NET application talk to our API. However, since the domain model (contract) is all in Java, we had to translate all the Java classes to C# classes so that we can use Json.NET to serialize/deserialize JSON, but this quickly became time-consuming. In addition, whenver there is contract change in Java we likely have to do this for C# classes also.

I searched online and found out IKVMC can translate a jar to a DLL, so I tried it out, however, it's causing some Json.NET serialization problems.

For example

I have a Java object that looks like this:

public class ApiToken {

    private String apiKey;

    private String apiSecret;

    public String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    public String getApiSecret() {
        return apiSecret;
    }

    public void setApiSecret(String apiSecret) {
        this.apiSecret = apiSecret;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(apiKey, apiSecret);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ApiToken other = (ApiToken) obj;
        return Objects.equal(this.apiKey, other.apiKey) && Objects.equal(this.apiSecret, other.apiSecret);
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this).add("apiKey", apiKey).add("apiSecret", apiSecret).toString();
    }
}

AFTER translation by ikvmc, it looks like this:

public class ApiToken : Object
  {
    [LineNumberTable(11)]
    [MethodImpl(MethodImplOptions.NoInlining)]
    public ApiToken();
    public virtual string getApiKey();
    public virtual void setApiKey(string apiKey);
    public virtual string getApiSecret();
    public virtual void setApiSecret(string apiSecret);
    [LineNumberTable(35)]
    public override int hashCode();
    [LineNumberTable(new byte[] {159, 182, 100, 98, 99, 98, 110, 98, 103})]
    [MethodImpl(MethodImplOptions.NoInlining)]
    public override bool equals(object obj);
    [LineNumberTable(52)]
    public override string toString();
  }

when I create this object in C#, JSON.net does NOT seralize this object correctly. Instead, it just produces an empty JSON {}

I suspect this is because there are no fields/properties exposed in the object that is generated by ikvmc.

Does anyone know if there's a workaround for this?

Thanks a lot and much appreciated

Update: This is how I'm serializing the object

  ApiToken apiToken = new ApiToken();
  apiToken.setApiKey("test");
  apiToken.setApiSecret("secret");
  string json = JsonConvert.SerializeObject(apiToken);

The json output is {}.

È stato utile?

Soluzione

I suspect this is because there are no fields/properties exposed in the object that is generated by ikvmc

Yes.

Does anyone know if there's a workaround for this?

You can do it by writing a custom ContractResolver and ValueProvider as below

var obj = new ApiToken();
obj.setApiKey("X-X-X");
obj.setI(666);

var settings = new Newtonsoft.Json.JsonSerializerSettings() { 
                     ContractResolver = new MyContractResolver() 
               };

var json = JsonConvert.SerializeObject(obj, settings);
//json : {"ApiKey":"X-X-X","I":666}
var newobj = JsonConvert.DeserializeObject<ApiToken>(json, settings);

//Test class
public class ApiToken 
{
    private String apiKey;

    public String getApiKey()
    {
        return apiKey;
    }

    public void setApiKey(String apiKey)
    {
        this.apiKey = apiKey;
    }


    private int i;

    public int getI()
    {
        return i;
    }

    public void setI(int i)
    {
        this.i = i;
    }

    public string dummy()
    {
        return "abcde";
    }
}

public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
    protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
    {
        //Find methods.  setXXX getXXX
        var properties = type.GetMethods()
            .Where(m => m.Name.Length > 3)
            .GroupBy(m => m.Name.Substring(3))
            .Where(g => g.Count() == 2 && g.Any(x=>x.Name=="set" + g.Key) && g.Any(x=>x.Name=="get" + g.Key))
            .ToList();

        //Create a JsonProperty for each set/getXXX pair
        var ret = properties.Select(prop=>
                    {
                        var jProp = new Newtonsoft.Json.Serialization.JsonProperty();
                        jProp.PropertyName = prop.Key;
                        jProp.PropertyType = prop.First(m => m.Name.StartsWith("get")).ReturnType;
                        jProp.ValueProvider = new MyValueProvider(prop.ToList());
                        jProp.Readable = jProp.Writable = true;
                        return jProp;
                    })
                    .ToList();

        return ret;
    }
}

public class MyValueProvider : Newtonsoft.Json.Serialization.IValueProvider
{
    List<MethodInfo> _MethodInfos = null;
    public MyValueProvider(List<MethodInfo> methodInfos)
    {
        _MethodInfos = methodInfos;
    }
    public object GetValue(object target)
    {
        return _MethodInfos.First(m => m.Name.StartsWith("get")).Invoke(target, null);
    }

    public void SetValue(object target, object value)
    {
        _MethodInfos.First(m => m.Name.StartsWith("set")).Invoke(target, new object[] { value });
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top