Сериализация пар имени / значения в пользовательском объекте через веб-сервис - PullRequest
7 голосов
/ 01 ноября 2008

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

Объект хранения образцов:

[Serializable]
public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  public List<NameValuePairs> OtherInfo { get; set; }  
}
[Serializable]   
public class NameValuePairs {
  public string Name { get; set; }
  public string Value { get; set; }
}

Образец использования:

[WebMethod]
    public List<StorageObject> GetStorageObjects() {
      List<StorageObject> o = new List<StorageObject>() {
        new StorageObject() { 
          Name = "Matthew",
          Birthday = "Jan 1st, 2008", 
          OtherInfo = new List<NameValuePairs>() {
            new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
            new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
          }
        },
        new StorageObject() { 
          Name = "Joe",
          Birthday = "Jan 10th, 2008",
          OtherInfo = new List<NameValuePairs>() { 
            new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
            new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
          }
        }
      };

      return o;
    }

Возвращаемое значение из веб-службы:

<StorageObject>
    <Name>Matthew</Name>
    <Birthday>Jan 1st, 2008</Birthday>
    <OtherInfo>
        <NameValuePairs>
            <Name>Hobbies</Name>
            <Value>Programming</Value>
        </NameValuePairs>
        <NameValuePairs>
            <Name>Website</Name>
            <Value>Stackoverflow.com</Value>
        </NameValuePairs>
    </OtherInfo>
</StorageObject>

Что я хочу:

<OtherInfo>
    <Hobbies>Programming</Hobbies>
    <Website>Stackoverflow.com</Website>
</OtherInfo>

Причина и прочее:

Во-первых, я извиняюсь за длину поста, но я также хотел дать воспроизводимый код.

Я хочу это в этом формате, потому что я использую веб-сервисы из PHP. Я хочу легко пойти:

// ЭТО ВАЖНО

In PHP => "$Result["StorageObject"]["OtherInfo"]["Hobbies"]".  

Если бы он был в другом формате, то у меня не было бы возможности сделать это вообще. Кроме того, в C #, если я использую службу, я также хотел бы сделать следующее:

// ЭТО ВАЖНО

In C# => var m = ServiceResult[0].OtherInfo["Hobbies"];

К сожалению, я не уверен, как этого добиться. Я смог получить это таким образом, создав собственный словарь, в котором реализован IXmlSerializer (см. StackOverflow: словарь IXmlSerializer ), однако он взорвал схему WSDL из воды. Это также слишком сложно и дало ужасные результаты в моем приложении WinFormsTester!

Есть ли способ сделать это? Какой тип объектов мне нужно создать? Есть ли способ сделать это / кроме создания строго типизированной коллекции /? Очевидно, что если я сделаю это строго напечатано так:

public class OtherInfo {
  public string Hobbies { get; set; }
  public string FavoriteWebsite { get; set; }
}

Тогда это будет работать отлично, у меня не будет проблем с WSDL, я смогу легко получить к нему доступ из PHP и C # (.OtherInfo.Hobbies).

Однако я бы полностью потерял смысл NVP, потому что мне нужно было бы заранее знать, что это за список, и он был бы неизменным ... скажем, из базы данных.

Спасибо всем !! Я надеюсь, что мы сможем найти какое-то решение для этого. Вот требования снова:

  1. Схема WSDL не должна ломаться
  2. Пары имя-значение (NVP) должны быть сериализованы в формат атрибута
  3. Должно быть легко получить доступ к NVP в PHP по имени ["Хобби"]
  4. Должен быть легкодоступным в C # (и быть совместимым с генератором прокси)
  5. Быть легко сериализуемым
  6. Не требуется, чтобы я строго печатал данные

Теперь я / полностью / открыт для ввода лучшего / другого способа сделать это. Я храню некоторую относительно «статическую» информацию (например, Имя) и кучу данных. Если есть лучший способ, я бы с удовольствием его услышал.

Ответы [ 5 ]

5 голосов
/ 01 ноября 2008

Это похоже на динамические свойства объекта. C # не совсем динамический язык в отличие от javascript или, может быть, PHP может анализировать свойства объекта на лету. Следующие два метода - это то, о чем я могу думать. Второй может соответствовать вашим требованиям.

ПОЦЕЛУЙ ПУТЬ

Не усложняй глупый путь

public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  public List<string> OtherInfo { get; set; }  
}

У вас могут быть пары имя-значение, разделенные '|'

OtherInfo = {"Hobbies|Programming", "Website|Stackoverflow.com"}

Сериализованные формы

<StorageObject>
    <Name>Matthew</Name>
    <Birthday>Jan 1st, 2008</Birthday>
    <OtherInfo>
        <string>Hobbies|Programming</string>
        <string>Website|Stackoverflow.com</string>
    </OtherInfo>
</StorageObject>

Динамический путь в C #

Сделайте так, чтобы пара имен-значений стала элементом XML, чтобы вы могли строить его динамически.

public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  public XElement OtherInfo { get; set; } // XmlElement for dot net 2
}

Вы можете легко создать объект OtherInfo как элемент, ориентированный например

XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..Hobbies xelement & text value..);
OtherInfo.Add( ..WebSite xelement & text value..);

Сериализованная форма будет

<OtherInfo>
    <Hobbies>Programming</Hobbies>
    <Website>Stackoverflow.com</Website>
</OtherInfo>

или создайте его как атрибут, ориентированный на

XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..nvp xattribute Hobbies & value..);
OtherInfo.Add( ..nvp xattribute WebSite & value..);

<OtherInfo>
    <nvp n="Hobbies" v="Programming" />
    <nvp n="Website" v="Stackoverflow.com" />
</OtherInfo>

Для любого динамического языка он может напрямую обращаться к свойствам. В остальном они могут получить доступ к значению, прочитав XML. Чтение XML хорошо поддерживается большинством фреймворков.

4 голосов
/ 01 ноября 2008

На этом я остановился.

Структура класса:

public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }
  [XmlAnyElement("Info")]  // this prevents double-nodes in the XML
  public XElement OtherInfo { get; set; }
}

Использование:

StorageObject o = new StorageObject();
o.OtherInfo.Add(new XElement("Hobbies","Programming");
o.OtherInfo.Add(new XElement("Website","Stackoverflow.com");

Выход:

<Info>
  <Hobbies>Programming</Hobbies>
  <Website>Stackoverflow.com</Website>
</Info>

Я хотел бы поблагодарить всех за помощь, я очень ценю помощь и идеи.

1 голос
/ 01 ноября 2008

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

[Serializable]
public class StorageObject {
  public string Name { get; set; }
  public string Birthday { get; set; }

  [Nonserializable]
  public Dictionary<string,List<string>> OtherInfo { get; set; }  
}

[WebMethod]
public List<StorageObject> GetStorageObjects() {
    // returns list of storage objects from persistent storage or cache
}

[WebMethod]
public List<string> GetStorageObjectAttributes( string name )
{
    // find storage object, sObj
    return sObj.Keys.ToList();
}

[WebMethod]
public List<string> GetStorageObjectAtributeValues( sting name, string attribute )
{
    // find storage object, sObj
    return sObj[attribute];
}
0 голосов
/ 01 ноября 2008

Просмотрите атрибут System.Xml.Serialization.XmlSerializerAssemblyAttribute. Это позволяет вам указать пользовательский сериализатор уровня класса. Вы сможете выплевывать любой XML, который вам нравится.

Быстрый способ освоить их - использовать sgen.exe для его создания и взглянуть на него с помощью Reflector.

-Oisin

0 голосов
/ 01 ноября 2008

Я не уверен, что это решит вашу проблему (это будет в C #, но, возможно, не в PHP), но попробуйте использовать Dictionary<string,List<string>> OtherInfo вместо List<NameValuePairs>. Тогда вашими ключами будут «Хобби» и «Веб-сайты», а значениями - список хобби или веб-сайтов. Я не уверен, как это будет сериализовать.

Вы можете ссылаться на списки хобби как:

List<string> hobbies = storageObject.OtherInfo["Hobbies"];

[EDIT] Смотрите здесь для общего XML-сериализуемого словаря. Этот производный класс вам нужно использовать вместо универсального словаря.

...