Как исправить веб-прокси-сервер ссылок, сгенерированный Visual Studio, для обработки неровных массивов? - PullRequest
5 голосов
/ 06 августа 2010

Кажется, есть известная ошибка в wsdl.exe, инструменте, который Visual Studio использует для генерации прокси веб-сервисов. С некоторыми схемами XSD инструмент генерирует классы, которые не могут быть десериализованы из XML.

Насколько я понимаю, это неприемлемо, но я не знаю, как это исправить.

Я опишу свой случай подробно, надеюсь, кто-нибудь сможет мне помочь с этим.

Схема

<!-- return type from the service operation -->
<xs:complexType name="listAssetsQueryResults">
    <xs:sequence>
        <xs:element name="assets" type="tns:asset" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>

<!-- a sequence of attributes -->
<xs:complexType name="asset">
    <xs:sequence>
        <xs:element name="attributes" type="tns:multiValuedAttribute" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>

<xs:complexType name="multiValuedAttribute">
    <!-- not relevant-->
</xs:complexType>

XML-ответ от веб-службы

Типичный ответ по этой схеме выглядит следующим образом:

<assets-query-result>
    <assets>
        <attributes>
            <name>Keywords</name>
            <values>Desert</values>
        </attributes>
        <attributes>
            <name>Filename</name>
            <values>Desert.jpg</values>
        </attributes>
    </assets>
    <assets>...</assets>
    <assets>...</assets>
</assets-query-result>

Использование типов в коде

Я бы ожидал, что смогу использовать типы CLR следующим образом:

result.assets[0].attributes[0].name

Вместо этого сгенерированный тип для результата выглядит следующим образом:

[SerializableAttribute()]
public partial class listAssetsQueryResults {
    private multiValuedAttribute[][] assetsField;

    [XmlArrayAttribute(Form=XmlSchemaForm.Unqualified, IsNullable=true)]
    [XmlArrayItemAttribute("attributes", typeof(multiValuedAttribute), Form=XmlSchemaForm.Unqualified)]
    public multiValuedAttribute[][] assets {
        get { return this.assetsField; }
        set { this.assetsField = value; }
    }
}

Что даже не позволяет генерировать сборку сериализации!

Невозможно преобразовать тип Portfolio.WebService.multiValuedAttribute to Portfolio.WebService.multiValuedAttribute []

Исправление

1 - Изменение типа свойства и поля

Теперь одно из исправлений, которые я нашел в Интернете, - просто удалить одну пару скобок из типа сгенерированного свойства:

// No longer a jagged array, but this doesn't deserialize all data
public multiValuedAttribute[] assets;

Это позволяет создавать сборку сериализации, и она работает без исключений, за исключением того, что она не сериализует данные правильно, она «пропускает» список ресурсов и десериализует атрибуты первого элемента assets. Так что это совсем не исправление, потому что с этим исправлением я не могу использовать данные. Для 700+ активов он дает result.assets, равный multiValuedAttribute[2] (2 элемента - это атрибуты имени и ключевых слов первого актива).

2 - указание типа XML-элементов

Второе, что я попробовал, это дать десериализатору разные инструкции:

[XmlArrayItemAttribute("attributes", typeof(multiValuedAttribute[]), Form=XmlSchemaForm.Unqualified)]
public multiValuedAttribute[][] assets { ... }

Итак, я говорю, что каждый элемент в последовательности имеет тип multiValuedAttribute[]. Это неправильно, потому что он по-прежнему смотрит на attributes элементы, которые имеют тип multiValuedAttribute (один, а не массив). Однако он работает, но теперь result.assets равен multiValuedAttribute[2][0], и я все еще не могу получить данные.

Что дальше?

Понятия не имею, поэтому я и написал это. Я не могу согласиться с тем, что .NET не может использовать этот веб-сервис, потому что он должен.

Ответы [ 3 ]

3 голосов
/ 17 августа 2010

Я думаю, что вы должны определить отдельный класс Asset, который будет иметь свойство типа multiValuedAttribute [].Таким образом, он будет выглядеть примерно так:

public class Asset
{
   public multiValuedAttribute[] attributes {get; set;}
}

public partial class listAssetsQueryResults {
    private Asset[] assetsField;

    public Asset[] assets {

Затем вам нужно украсить тип актива, атрибуты и свойство активов с помощью некоторой комбинации атрибутов XmlElement / XmlArrayElement / XmlArrayItemElement, чтобы заставить его работать.

Нет необходимостискажем, в любое время, когда вам нужно заново сгенерировать свой прокси-код, вы должны повторно применить вышеуказанные изменения (возможно, вы можете сделать пакетный скрипт для этого в качестве действия сборки).

2 голосов
/ 25 сентября 2013

Эта проблема очень типична при использовании веб-сервисов SOAP, которые были написаны на Java, а затем использованы WCF, C #, .Net и т. Д., Где используются зубчатые массивы (массивы массивов). Пост VinayC помог, но вот, например, более явный код, который может помочь другим, кто столкнулся с этой проблемой.

Обратите внимание, что эти классы сокращены. Ваш сгенерированный WSDL-код, безусловно, будет выглядеть сложнее.

public partial class assests{
  private multiValuedAttribute[] attributesField;
  [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public multiValuedAttribute[] attributes{
    get {return this.attributesField;}
    set {this.attributesField = value;}
  }
}

public partial class listAssetsQueryResults{
  private assests[] assetsField;
  [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public assets[] assets{
      get {return this.assetsField;}
      set {this.assetsField = value;}
  }
}

Ключевым моментом здесь является создание нового класса 'assets', который является связующим звеном между классами multiValuedAttribute и listAssetsQueryResults, а затем сериализует поле с помощью XmlElementAttribute. Хороший способ сделать это в ваших собственных проектах - начать с копирования и вставки класса, аналогичного классу multiValuedAttribute (хотя реализация этого класса здесь не показана). Затем просто переименуйте скопированный класс multiValuedAttribute в «assets» и измените реализацию, чтобы она вместо этого работала как связующее звено между ними. Удалите лишние [] из операторов, которые содержат [] [] в классе listAssetsQueryResults. Сериализация с использованием XmlElement вместо XmlArrayItem.

Конечно, разные сервисы предъявляют разные требования. Приложение Fiddler (http://fiddler2.com/) действительно может помочь проверить сериализацию, чтобы убедиться, что вы все правильно поняли. Fiddler действительно очень помог мне. Вот еще кое-что, что мне тоже помогло: http://msdn.microsoft.com/en-us/library/2baksw0z.aspx

0 голосов
/ 06 августа 2010

Вы всегда можете изменить тип так, чтобы веб-сервис возвращал что-то дружественное процессору wsdl.Так, например, основываясь на приведенном вами примере, вы легко сможете преобразовать его в массив KeyValuePair<string, string> или что-то еще до его возврата из веб-службы.

В любом случае, это, вероятно, лучший API, чем показ зубчатых массивов.

Я не могу согласиться с тем, что .NET не может использовать этот веб-сервис

Ну, как говорит моя подруга Мать Тереза: «Жизнь - это борьба, прими ее».; -)

...